/* This file is automatically rebuilt by the Cesium build process. */
define(['exports', './when-e6e3e713', './Check-1df6b9a0', './Math-c5f6c994', './Cartesian2-1d7364fa', './Transforms-943e8463', './ComponentDatatype-2b8834a4', './GeometryAttribute-3a303898', './AttributeCompression-d68d64ef', './EncodedCartesian3-d723731d', './IndexDatatype-e2961542', './IntersectionTests-c05f88ce', './Plane-2e419ea5'], function (exports, when, Check, _Math, Cartesian2, Transforms, ComponentDatatype, GeometryAttribute, AttributeCompression, EncodedCartesian3, IndexDatatype, IntersectionTests, Plane) { 'use strict';

    var scratchCartesian1 = new Cartesian2.Cartesian3();
        var scratchCartesian2 = new Cartesian2.Cartesian3();
        var scratchCartesian3 = new Cartesian2.Cartesian3();

        /**
         * Computes the barycentric coordinates for a point with respect to a triangle.
         *
         * @exports barycentricCoordinates
         *
         * @param {Cartesian2|Cartesian3} point The point to test.
         * @param {Cartesian2|Cartesian3} p0 The first point of the triangle, corresponding to the barycentric x-axis.
         * @param {Cartesian2|Cartesian3} p1 The second point of the triangle, corresponding to the barycentric y-axis.
         * @param {Cartesian2|Cartesian3} p2 The third point of the triangle, corresponding to the barycentric z-axis.
         * @param {Cartesian3} [result] The object onto which to store the result.
         * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
         *
         * @example
         * // Returns Cartesian3.UNIT_X
         * var p = new Cesium.Cartesian3(-1.0, 0.0, 0.0);
         * var b = Cesium.barycentricCoordinates(p,
         *   new Cesium.Cartesian3(-1.0, 0.0, 0.0),
         *   new Cesium.Cartesian3( 1.0, 0.0, 0.0),
         *   new Cesium.Cartesian3( 0.0, 1.0, 1.0));
         */
        function barycentricCoordinates(point, p0, p1, p2, result) {
            //>>includeStart('debug', pragmas.debug);
            Check.Check.defined('point', point);
            Check.Check.defined('p0', p0);
            Check.Check.defined('p1', p1);
            Check.Check.defined('p2', p2);
            //>>includeEnd('debug');

            if (!when.defined(result)) {
                result = new Cartesian2.Cartesian3();
            }

            // Implementation based on http://www.blackpawn.com/texts/pointinpoly/default.html.
            var v0;
            var v1;
            var v2;
            var dot00;
            var dot01;
            var dot02;
            var dot11;
            var dot12;

            if(!when.defined(p0.z)) {
                if (Cartesian2.Cartesian2.equalsEpsilon(point, p0, _Math.CesiumMath.EPSILON14)) {
                    return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_X, result);
                }
                if (Cartesian2.Cartesian2.equalsEpsilon(point, p1, _Math.CesiumMath.EPSILON14)) {
                    return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Y, result);
                }
                if (Cartesian2.Cartesian2.equalsEpsilon(point, p2, _Math.CesiumMath.EPSILON14)) {
                    return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Z, result);
                }

                v0 = Cartesian2.Cartesian2.subtract(p1, p0, scratchCartesian1);
                v1 = Cartesian2.Cartesian2.subtract(p2, p0, scratchCartesian2);
                v2 = Cartesian2.Cartesian2.subtract(point, p0, scratchCartesian3);

                dot00 = Cartesian2.Cartesian2.dot(v0, v0);
                dot01 = Cartesian2.Cartesian2.dot(v0, v1);
                dot02 = Cartesian2.Cartesian2.dot(v0, v2);
                dot11 = Cartesian2.Cartesian2.dot(v1, v1);
                dot12 = Cartesian2.Cartesian2.dot(v1, v2);
            } else {
                if (Cartesian2.Cartesian3.equalsEpsilon(point, p0, _Math.CesiumMath.EPSILON14)) {
                    return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_X, result);
                }
                if (Cartesian2.Cartesian3.equalsEpsilon(point, p1, _Math.CesiumMath.EPSILON14)) {
                    return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Y, result);
                }
                if (Cartesian2.Cartesian3.equalsEpsilon(point, p2, _Math.CesiumMath.EPSILON14)) {
                    return Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.UNIT_Z, result);
                }

                v0 = Cartesian2.Cartesian3.subtract(p1, p0, scratchCartesian1);
                v1 = Cartesian2.Cartesian3.subtract(p2, p0, scratchCartesian2);
                v2 = Cartesian2.Cartesian3.subtract(point, p0, scratchCartesian3);

                dot00 = Cartesian2.Cartesian3.dot(v0, v0);
                dot01 = Cartesian2.Cartesian3.dot(v0, v1);
                dot02 = Cartesian2.Cartesian3.dot(v0, v2);
                dot11 = Cartesian2.Cartesian3.dot(v1, v1);
                dot12 = Cartesian2.Cartesian3.dot(v1, v2);
            }

            result.y = (dot11 * dot02 - dot01 * dot12);
            result.z = (dot00 * dot12 - dot01 * dot02);
            var q = dot00 * dot11 - dot01 * dot01;

            // This is done to avoid dividing by infinity causing a NaN
            if (result.y !== 0) {
                result.y /= q;
            }
            if (result.z !== 0) {
                result.z /= q;
            }

            result.x = 1.0 - result.y - result.z;
            return result;
        }

    /**
         * Encapsulates an algorithm to optimize triangles for the post
         * vertex-shader cache.  This is based on the 2007 SIGGRAPH paper
         * 'Fast Triangle Reordering for Vertex Locality and Reduced Overdraw.'
         * The runtime is linear but several passes are made.
         *
         * @exports Tipsify
         *
         * @see <a href='http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf'>
         * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw</a>
         * by Sander, Nehab, and Barczak
         *
         * @private
         */
        var Tipsify = {};

        /**
         * Calculates the average cache miss ratio (ACMR) for a given set of indices.
         *
         * @param {Object} options Object with the following properties:
         * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices
         *                        in the vertex buffer that define the geometry's triangles.
         * @param {Number} [options.maximumIndex] The maximum value of the elements in <code>args.indices</code>.
         *                                     If not supplied, this value will be computed.
         * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time.
         * @returns {Number} The average cache miss ratio (ACMR).
         *
         * @exception {DeveloperError} indices length must be a multiple of three.
         * @exception {DeveloperError} cacheSize must be greater than two.
         *
         * @example
         * var indices = [0, 1, 2, 3, 4, 5];
         * var maxIndex = 5;
         * var cacheSize = 3;
         * var acmr = Cesium.Tipsify.calculateACMR({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize});
         */
        Tipsify.calculateACMR = function(options) {
            options = when.defaultValue(options, when.defaultValue.EMPTY_OBJECT);
            var indices = options.indices;
            var maximumIndex = options.maximumIndex;
            var cacheSize = when.defaultValue(options.cacheSize, 24);

            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(indices)) {
                throw new Check.DeveloperError('indices is required.');
            }
            //>>includeEnd('debug');

            var numIndices = indices.length;

            //>>includeStart('debug', pragmas.debug);
            if (numIndices < 3 || numIndices % 3 !== 0) {
                throw new Check.DeveloperError('indices length must be a multiple of three.');
            }
            if (maximumIndex <= 0) {
                throw new Check.DeveloperError('maximumIndex must be greater than zero.');
            }
            if (cacheSize < 3) {
                throw new Check.DeveloperError('cacheSize must be greater than two.');
            }
            //>>includeEnd('debug');

            // Compute the maximumIndex if not given
            if (!when.defined(maximumIndex)) {
                maximumIndex = 0;
                var currentIndex = 0;
                var intoIndices = indices[currentIndex];
                while (currentIndex < numIndices) {
                    if (intoIndices > maximumIndex) {
                        maximumIndex = intoIndices;
                    }
                    ++currentIndex;
                    intoIndices = indices[currentIndex];
                }
            }

            // Vertex time stamps
            var vertexTimeStamps = [];
            for ( var i = 0; i < maximumIndex + 1; i++) {
                vertexTimeStamps[i] = 0;
            }

            // Cache processing
            var s = cacheSize + 1;
            for ( var j = 0; j < numIndices; ++j) {
                if ((s - vertexTimeStamps[indices[j]]) > cacheSize) {
                    vertexTimeStamps[indices[j]] = s;
                    ++s;
                }
            }

            return (s - cacheSize + 1) / (numIndices / 3);
        };

        /**
         * Optimizes triangles for the post-vertex shader cache.
         *
         * @param {Object} options Object with the following properties:
         * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices
         *                        in the vertex buffer that define the geometry's triangles.
         * @param {Number} [options.maximumIndex] The maximum value of the elements in <code>args.indices</code>.
         *                                     If not supplied, this value will be computed.
         * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time.
         * @returns {Number[]} A list of the input indices in an optimized order.
         *
         * @exception {DeveloperError} indices length must be a multiple of three.
         * @exception {DeveloperError} cacheSize must be greater than two.
         *
         * @example
         * var indices = [0, 1, 2, 3, 4, 5];
         * var maxIndex = 5;
         * var cacheSize = 3;
         * var reorderedIndices = Cesium.Tipsify.tipsify({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize});
         */
        Tipsify.tipsify = function(options) {
            options = when.defaultValue(options, when.defaultValue.EMPTY_OBJECT);
            var indices = options.indices;
            var maximumIndex = options.maximumIndex;
            var cacheSize = when.defaultValue(options.cacheSize, 24);

            var cursor;

            function skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne) {
                while (deadEnd.length >= 1) {
                    // while the stack is not empty
                    var d = deadEnd[deadEnd.length - 1]; // top of the stack
                    deadEnd.splice(deadEnd.length - 1, 1); // pop the stack

                    if (vertices[d].numLiveTriangles > 0) {
                        return d;
                    }
                }

                while (cursor < maximumIndexPlusOne) {
                    if (vertices[cursor].numLiveTriangles > 0) {
                        ++cursor;
                        return cursor - 1;
                    }
                    ++cursor;
                }
                return -1;
            }

            function getNextVertex(indices, cacheSize, oneRing, vertices, s, deadEnd, maximumIndexPlusOne) {
                var n = -1;
                var p;
                var m = -1;
                var itOneRing = 0;
                while (itOneRing < oneRing.length) {
                    var index = oneRing[itOneRing];
                    if (vertices[index].numLiveTriangles) {
                        p = 0;
                        if ((s - vertices[index].timeStamp + (2 * vertices[index].numLiveTriangles)) <= cacheSize) {
                            p = s - vertices[index].timeStamp;
                        }
                        if ((p > m) || (m === -1)) {
                            m = p;
                            n = index;
                        }
                    }
                    ++itOneRing;
                }
                if (n === -1) {
                    return skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne);
                }
                return n;
            }

            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(indices)) {
                throw new Check.DeveloperError('indices is required.');
            }
            //>>includeEnd('debug');

            var numIndices = indices.length;

            //>>includeStart('debug', pragmas.debug);
            if (numIndices < 3 || numIndices % 3 !== 0) {
                throw new Check.DeveloperError('indices length must be a multiple of three.');
            }
            if (maximumIndex <= 0) {
                throw new Check.DeveloperError('maximumIndex must be greater than zero.');
            }
            if (cacheSize < 3) {
                throw new Check.DeveloperError('cacheSize must be greater than two.');
            }
            //>>includeEnd('debug');

            // Determine maximum index
            var maximumIndexPlusOne = 0;
            var currentIndex = 0;
            var intoIndices = indices[currentIndex];
            var endIndex = numIndices;
            if (when.defined(maximumIndex)) {
                maximumIndexPlusOne = maximumIndex + 1;
            } else {
                while (currentIndex < endIndex) {
                    if (intoIndices > maximumIndexPlusOne) {
                        maximumIndexPlusOne = intoIndices;
                    }
                    ++currentIndex;
                    intoIndices = indices[currentIndex];
                }
                if (maximumIndexPlusOne === -1) {
                    return 0;
                }
                ++maximumIndexPlusOne;
            }

            // Vertices
            var vertices = [];
            var i;
            for (i = 0; i < maximumIndexPlusOne; i++) {
                vertices[i] = {
                    numLiveTriangles : 0,
                    timeStamp : 0,
                    vertexTriangles : []
                };
            }
            currentIndex = 0;
            var triangle = 0;
            while (currentIndex < endIndex) {
                vertices[indices[currentIndex]].vertexTriangles.push(triangle);
                ++(vertices[indices[currentIndex]]).numLiveTriangles;
                vertices[indices[currentIndex + 1]].vertexTriangles.push(triangle);
                ++(vertices[indices[currentIndex + 1]]).numLiveTriangles;
                vertices[indices[currentIndex + 2]].vertexTriangles.push(triangle);
                ++(vertices[indices[currentIndex + 2]]).numLiveTriangles;
                ++triangle;
                currentIndex += 3;
            }

            // Starting index
            var f = 0;

            // Time Stamp
            var s = cacheSize + 1;
            cursor = 1;

            // Process
            var oneRing = [];
            var deadEnd = []; //Stack
            var vertex;
            var intoVertices;
            var currentOutputIndex = 0;
            var outputIndices = [];
            var numTriangles = numIndices / 3;
            var triangleEmitted = [];
            for (i = 0; i < numTriangles; i++) {
                triangleEmitted[i] = false;
            }
            var index;
            var limit;
            while (f !== -1) {
                oneRing = [];
                intoVertices = vertices[f];
                limit = intoVertices.vertexTriangles.length;
                for ( var k = 0; k < limit; ++k) {
                    triangle = intoVertices.vertexTriangles[k];
                    if (!triangleEmitted[triangle]) {
                        triangleEmitted[triangle] = true;
                        currentIndex = triangle + triangle + triangle;
                        for ( var j = 0; j < 3; ++j) {
                            // Set this index as a possible next index
                            index = indices[currentIndex];
                            oneRing.push(index);
                            deadEnd.push(index);

                            // Output index
                            outputIndices[currentOutputIndex] = index;
                            ++currentOutputIndex;

                            // Cache processing
                            vertex = vertices[index];
                            --vertex.numLiveTriangles;
                            if ((s - vertex.timeStamp) > cacheSize) {
                                vertex.timeStamp = s;
                                ++s;
                            }
                            ++currentIndex;
                        }
                    }
                }
                f = getNextVertex(indices, cacheSize, oneRing, vertices, s, deadEnd, maximumIndexPlusOne);
            }

            return outputIndices;
        };

    /**
         * Content pipeline functions for geometries.
         *
         * @exports GeometryPipeline
         *
         * @see Geometry
         */
        var GeometryPipeline = {};

        function addTriangle(lines, index, i0, i1, i2) {
            lines[index++] = i0;
            lines[index++] = i1;

            lines[index++] = i1;
            lines[index++] = i2;

            lines[index++] = i2;
            lines[index] = i0;
        }

        function trianglesToLines(triangles) {
            var count = triangles.length;
            var size = (count / 3) * 6;
            var lines = IndexDatatype.IndexDatatype.createTypedArray(count, size);

            var index = 0;
            for ( var i = 0; i < count; i += 3, index += 6) {
                addTriangle(lines, index, triangles[i], triangles[i + 1], triangles[i + 2]);
            }

            return lines;
        }

        function triangleStripToLines(triangles) {
            var count = triangles.length;
            if (count >= 3) {
                var size = (count - 2) * 6;
                var lines = IndexDatatype.IndexDatatype.createTypedArray(count, size);

                addTriangle(lines, 0, triangles[0], triangles[1], triangles[2]);
                var index = 6;

                for ( var i = 3; i < count; ++i, index += 6) {
                    addTriangle(lines, index, triangles[i - 1], triangles[i], triangles[i - 2]);
                }

                return lines;
            }

            return new Uint16Array();
        }

        function triangleFanToLines(triangles) {
            if (triangles.length > 0) {
                var count = triangles.length - 1;
                var size = (count - 1) * 6;
                var lines = IndexDatatype.IndexDatatype.createTypedArray(count, size);

                var base = triangles[0];
                var index = 0;
                for ( var i = 1; i < count; ++i, index += 6) {
                    addTriangle(lines, index, base, triangles[i], triangles[i + 1]);
                }

                return lines;
            }

            return new Uint16Array();
        }

        /**
         * Converts a geometry's triangle indices to line indices.  If the geometry has an <code>indices</code>
         * and its <code>primitiveType</code> is <code>TRIANGLES</code>, <code>TRIANGLE_STRIP</code>,
         * <code>TRIANGLE_FAN</code>, it is converted to <code>LINES</code>; otherwise, the geometry is not changed.
         * <p>
         * This is commonly used to create a wireframe geometry for visual debugging.
         * </p>
         *
         * @param {Geometry} geometry The geometry to modify.
         * @returns {Geometry} The modified <code>geometry</code> argument, with its triangle indices converted to lines.
         *
         * @exception {DeveloperError} geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.
         *
         * @example
         * geometry = Cesium.GeometryPipeline.toWireframe(geometry);
         */
        GeometryPipeline.toWireframe = function(geometry) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            //>>includeEnd('debug');

            var indices = geometry.indices;
            if (when.defined(indices)) {
                switch (geometry.primitiveType) {
                    case GeometryAttribute.PrimitiveType.TRIANGLES:
                        geometry.indices = trianglesToLines(indices);
                        break;
                    case GeometryAttribute.PrimitiveType.TRIANGLE_STRIP:
                        geometry.indices = triangleStripToLines(indices);
                        break;
                    case GeometryAttribute.PrimitiveType.TRIANGLE_FAN:
                        geometry.indices = triangleFanToLines(indices);
                        break;
                    //>>includeStart('debug', pragmas.debug);
                    default:
                        throw new Check.DeveloperError('geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.');
                    //>>includeEnd('debug');
                }

                geometry.primitiveType = GeometryAttribute.PrimitiveType.LINES;
            }

            return geometry;
        };

        /**
         * Creates a new {@link Geometry} with <code>LINES</code> representing the provided
         * attribute (<code>attributeName</code>) for the provided geometry.  This is used to
         * visualize vector attributes like normals, tangents, and bitangents.
         *
         * @param {Geometry} geometry The <code>Geometry</code> instance with the attribute.
         * @param {String} [attributeName='normal'] The name of the attribute.
         * @param {Number} [length=10000.0] The length of each line segment in meters.  This can be negative to point the vector in the opposite direction.
         * @returns {Geometry} A new <code>Geometry</code> instance with line segments for the vector.
         *
         * @exception {DeveloperError} geometry.attributes must have an attribute with the same name as the attributeName parameter.
         *
         * @example
         * var geometry = Cesium.GeometryPipeline.createLineSegmentsForVectors(instance.geometry, 'bitangent', 100000.0);
         */
        GeometryPipeline.createLineSegmentsForVectors = function(geometry, attributeName, length) {
            attributeName = when.defaultValue(attributeName, 'normal');

            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            if (!when.defined(geometry.attributes.position)) {
                throw new Check.DeveloperError('geometry.attributes.position is required.');
            }
            if (!when.defined(geometry.attributes[attributeName])) {
                throw new Check.DeveloperError('geometry.attributes must have an attribute with the same name as the attributeName parameter, ' + attributeName + '.');
            }
            //>>includeEnd('debug');

            length = when.defaultValue(length, 10000.0);

            var positions = geometry.attributes.position.values;
            var vectors = geometry.attributes[attributeName].values;
            var positionsLength = positions.length;

            var newPositions = new Float64Array(2 * positionsLength);

            var j = 0;
            for (var i = 0; i < positionsLength; i += 3) {
                newPositions[j++] = positions[i];
                newPositions[j++] = positions[i + 1];
                newPositions[j++] = positions[i + 2];

                newPositions[j++] = positions[i] + (vectors[i] * length);
                newPositions[j++] = positions[i + 1] + (vectors[i + 1] * length);
                newPositions[j++] = positions[i + 2] + (vectors[i + 2] * length);
            }

            var newBoundingSphere;
            var bs = geometry.boundingSphere;
            if (when.defined(bs)) {
                newBoundingSphere = new Transforms.BoundingSphere(bs.center, bs.radius + length);
            }

            return new GeometryAttribute.Geometry({
                attributes : {
                    position : new GeometryAttribute.GeometryAttribute({
                        componentDatatype : ComponentDatatype.ComponentDatatype.DOUBLE,
                        componentsPerAttribute : 3,
                        values : newPositions
                    })
                },
                primitiveType : GeometryAttribute.PrimitiveType.LINES,
                boundingSphere : newBoundingSphere
            });
        };

        /**
         * Creates an object that maps attribute names to unique locations (indices)
         * for matching vertex attributes and shader programs.
         *
         * @param {Geometry} geometry The geometry, which is not modified, to create the object for.
         * @returns {Object} An object with attribute name / index pairs.
         *
         * @example
         * var attributeLocations = Cesium.GeometryPipeline.createAttributeLocations(geometry);
         * // Example output
         * // {
         * //   'position' : 0,
         * //   'normal' : 1
         * // }
         */
        GeometryPipeline.createAttributeLocations = function(geometry) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            //>>includeEnd('debug')

            // There can be a WebGL performance hit when attribute 0 is disabled, so
            // assign attribute locations to well-known attributes.
            var semantics = [
                'position',
                'positionHigh',
                'positionLow',

                // From VertexFormat.position - after 2D projection and high-precision encoding
                'position3DHigh',
                'position3DLow',
                'position2DHigh',
                'position2DLow',

                // From Primitive
                'pickColor',

                // From VertexFormat
                'normal',
                'st',
                'tangent',
                'bitangent',

                // For shadow volumes
                'extrudeDirection',

                // From compressing texture coordinates and normals
                'compressedAttributes'
            ];

            var attributes = geometry.attributes;
            var indices = {};
            var j = 0;
            var i;
            var len = semantics.length;

            // Attribute locations for well-known attributes
            for (i = 0; i < len; ++i) {
                var semantic = semantics[i];

                if (when.defined(attributes[semantic])) {
                    indices[semantic] = j++;
                }
            }

            // Locations for custom attributes
            for (var name in attributes) {
                if (attributes.hasOwnProperty(name) && (!when.defined(indices[name]))) {
                    indices[name] = j++;
                }
            }

            return indices;
        };

        /**
         * Reorders a geometry's attributes and <code>indices</code> to achieve better performance from the GPU's pre-vertex-shader cache.
         *
         * @param {Geometry} geometry The geometry to modify.
         * @returns {Geometry} The modified <code>geometry</code> argument, with its attributes and indices reordered for the GPU's pre-vertex-shader cache.
         *
         * @exception {DeveloperError} Each attribute array in geometry.attributes must have the same number of attributes.
         *
         *
         * @example
         * geometry = Cesium.GeometryPipeline.reorderForPreVertexCache(geometry);
         *
         * @see GeometryPipeline.reorderForPostVertexCache
         */
        GeometryPipeline.reorderForPreVertexCache = function(geometry) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            //>>includeEnd('debug');

            var numVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);

            var indices = geometry.indices;
            if (when.defined(indices)) {
                var indexCrossReferenceOldToNew = new Int32Array(numVertices);
                for ( var i = 0; i < numVertices; i++) {
                    indexCrossReferenceOldToNew[i] = -1;
                }

                // Construct cross reference and reorder indices
                var indicesIn = indices;
                var numIndices = indicesIn.length;
                var indicesOut = IndexDatatype.IndexDatatype.createTypedArray(numVertices, numIndices);

                var intoIndicesIn = 0;
                var intoIndicesOut = 0;
                var nextIndex = 0;
                var tempIndex;
                while (intoIndicesIn < numIndices) {
                    tempIndex = indexCrossReferenceOldToNew[indicesIn[intoIndicesIn]];
                    if (tempIndex !== -1) {
                        indicesOut[intoIndicesOut] = tempIndex;
                    } else {
                        tempIndex = indicesIn[intoIndicesIn];
                        indexCrossReferenceOldToNew[tempIndex] = nextIndex;

                        indicesOut[intoIndicesOut] = nextIndex;
                        ++nextIndex;
                    }
                    ++intoIndicesIn;
                    ++intoIndicesOut;
                }
                geometry.indices = indicesOut;

                // Reorder attributes
                var attributes = geometry.attributes;
                for ( var property in attributes) {
                    if (attributes.hasOwnProperty(property) &&
                            when.defined(attributes[property]) &&
                            when.defined(attributes[property].values)) {

                        var attribute = attributes[property];
                        var elementsIn = attribute.values;
                        var intoElementsIn = 0;
                        var numComponents = attribute.componentsPerAttribute;
                        var elementsOut = ComponentDatatype.ComponentDatatype.createTypedArray(attribute.componentDatatype, nextIndex * numComponents);
                        while (intoElementsIn < numVertices) {
                            var temp = indexCrossReferenceOldToNew[intoElementsIn];
                            if (temp !== -1) {
                                for (var j = 0; j < numComponents; j++) {
                                    elementsOut[numComponents * temp + j] = elementsIn[numComponents * intoElementsIn + j];
                                }
                            }
                            ++intoElementsIn;
                        }
                        attribute.values = elementsOut;
                    }
                }
            }

            return geometry;
        };

        /**
         * Reorders a geometry's <code>indices</code> to achieve better performance from the GPU's
         * post vertex-shader cache by using the Tipsify algorithm.  If the geometry <code>primitiveType</code>
         * is not <code>TRIANGLES</code> or the geometry does not have an <code>indices</code>, this function has no effect.
         *
         * @param {Geometry} geometry The geometry to modify.
         * @param {Number} [cacheCapacity=24] The number of vertices that can be held in the GPU's vertex cache.
         * @returns {Geometry} The modified <code>geometry</code> argument, with its indices reordered for the post-vertex-shader cache.
         *
         * @exception {DeveloperError} cacheCapacity must be greater than two.
         *
         *
         * @example
         * geometry = Cesium.GeometryPipeline.reorderForPostVertexCache(geometry);
         *
         * @see GeometryPipeline.reorderForPreVertexCache
         * @see {@link http://gfx.cs.princ0eton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf|Fast Triangle Reordering for Vertex Locality and Reduced Overdraw}
         * by Sander, Nehab, and Barczak
         */
        GeometryPipeline.reorderForPostVertexCache = function(geometry, cacheCapacity) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            //>>includeEnd('debug');

            var indices = geometry.indices;
            if ((geometry.primitiveType === GeometryAttribute.PrimitiveType.TRIANGLES) && (when.defined(indices))) {
                var numIndices = indices.length;
                var maximumIndex = 0;
                for ( var j = 0; j < numIndices; j++) {
                    if (indices[j] > maximumIndex) {
                        maximumIndex = indices[j];
                    }
                }
                geometry.indices = Tipsify.tipsify({
                    indices : indices,
                    maximumIndex : maximumIndex,
                    cacheSize : cacheCapacity
                });
            }

            return geometry;
        };

        function copyAttributesDescriptions(attributes) {
            var newAttributes = {};

            for ( var attribute in attributes) {
                if (attributes.hasOwnProperty(attribute) &&
                        when.defined(attributes[attribute]) &&
                        when.defined(attributes[attribute].values)) {

                    var attr = attributes[attribute];
                    newAttributes[attribute] = new GeometryAttribute.GeometryAttribute({
                        componentDatatype : attr.componentDatatype,
                        componentsPerAttribute : attr.componentsPerAttribute,
                        normalize : attr.normalize,
                        values : []
                    });
                }
            }

            return newAttributes;
        }

        function copyVertex(destinationAttributes, sourceAttributes, index) {
            for ( var attribute in sourceAttributes) {
                if (sourceAttributes.hasOwnProperty(attribute) &&
                        when.defined(sourceAttributes[attribute]) &&
                        when.defined(sourceAttributes[attribute].values)) {

                    var attr = sourceAttributes[attribute];

                    for ( var k = 0; k < attr.componentsPerAttribute; ++k) {
                        destinationAttributes[attribute].values.push(attr.values[(index * attr.componentsPerAttribute) + k]);
                    }
                }
            }
        }

        /**
         * Splits a geometry into multiple geometries, if necessary, to ensure that indices in the
         * <code>indices</code> fit into unsigned shorts.  This is used to meet the WebGL requirements
         * when unsigned int indices are not supported.
         * <p>
         * If the geometry does not have any <code>indices</code>, this function has no effect.
         * </p>
         *
         * @param {Geometry} geometry The geometry to be split into multiple geometries.
         * @returns {Geometry[]} An array of geometries, each with indices that fit into unsigned shorts.
         *
         * @exception {DeveloperError} geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS
         * @exception {DeveloperError} All geometry attribute lists must have the same number of attributes.
         *
         * @example
         * var geometries = Cesium.GeometryPipeline.fitToUnsignedShortIndices(geometry);
         */
        GeometryPipeline.fitToUnsignedShortIndices = function(geometry) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            if ((when.defined(geometry.indices)) &&
                ((geometry.primitiveType !== GeometryAttribute.PrimitiveType.TRIANGLES) &&
                 (geometry.primitiveType !== GeometryAttribute.PrimitiveType.LINES) &&
                 (geometry.primitiveType !== GeometryAttribute.PrimitiveType.POINTS))) {
                throw new Check.DeveloperError('geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS.');
            }
            //>>includeEnd('debug');

            var geometries = [];

            // If there's an index list and more than 64K attributes, it is possible that
            // some indices are outside the range of unsigned short [0, 64K - 1]
            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);
            if (when.defined(geometry.indices) && (numberOfVertices >= _Math.CesiumMath.SIXTY_FOUR_KILOBYTES)) {
                var oldToNewIndex = [];
                var newIndices = [];
                var currentIndex = 0;
                var newAttributes = copyAttributesDescriptions(geometry.attributes);

                var originalIndices = geometry.indices;
                var numberOfIndices = originalIndices.length;

                var indicesPerPrimitive;

                if (geometry.primitiveType === GeometryAttribute.PrimitiveType.TRIANGLES) {
                    indicesPerPrimitive = 3;
                } else if (geometry.primitiveType === GeometryAttribute.PrimitiveType.LINES) {
                    indicesPerPrimitive = 2;
                } else if (geometry.primitiveType === GeometryAttribute.PrimitiveType.POINTS) {
                    indicesPerPrimitive = 1;
                }

                for ( var j = 0; j < numberOfIndices; j += indicesPerPrimitive) {
                    for (var k = 0; k < indicesPerPrimitive; ++k) {
                        var x = originalIndices[j + k];
                        var i = oldToNewIndex[x];
                        if (!when.defined(i)) {
                            i = currentIndex++;
                            oldToNewIndex[x] = i;
                            copyVertex(newAttributes, geometry.attributes, x);
                        }
                        newIndices.push(i);
                    }

                    if (currentIndex + indicesPerPrimitive >= _Math.CesiumMath.SIXTY_FOUR_KILOBYTES) {
                        geometries.push(new GeometryAttribute.Geometry({
                            attributes : newAttributes,
                            indices : newIndices,
                            primitiveType : geometry.primitiveType,
                            boundingSphere : geometry.boundingSphere,
                            boundingSphereCV : geometry.boundingSphereCV
                        }));

                        // Reset for next vertex-array
                        oldToNewIndex = [];
                        newIndices = [];
                        currentIndex = 0;
                        newAttributes = copyAttributesDescriptions(geometry.attributes);
                    }
                }

                if (newIndices.length !== 0) {
                    geometries.push(new GeometryAttribute.Geometry({
                        attributes : newAttributes,
                        indices : newIndices,
                        primitiveType : geometry.primitiveType,
                        boundingSphere : geometry.boundingSphere,
                        boundingSphereCV : geometry.boundingSphereCV
                    }));
                }
            } else {
                // No need to split into multiple geometries
                geometries.push(geometry);
            }

            return geometries;
        };

        var scratchProjectTo2DCartesian3 = new Cartesian2.Cartesian3();
        var scratchProjectTo2DCartographic = new Cartesian2.Cartographic();

        /**
         * Projects a geometry's 3D <code>position</code> attribute to 2D, replacing the <code>position</code>
         * attribute with separate <code>position3D</code> and <code>position2D</code> attributes.
         * <p>
         * If the geometry does not have a <code>position</code>, this function has no effect.
         * </p>
         *
         * @param {Geometry} geometry The geometry to modify.
         * @param {String} attributeName The name of the attribute.
         * @param {String} attributeName3D The name of the attribute in 3D.
         * @param {String} attributeName2D The name of the attribute in 2D.
         * @param {Object} [projection=new GeographicProjection()] The projection to use.
         * @returns {Geometry} The modified <code>geometry</code> argument with <code>position3D</code> and <code>position2D</code> attributes.
         *
         * @exception {DeveloperError} geometry must have attribute matching the attributeName argument.
         * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE.
         * @exception {DeveloperError} Could not project a point to 2D.
         *
         * @example
         * geometry = Cesium.GeometryPipeline.projectTo2D(geometry, 'position', 'position3D', 'position2D');
         */
        GeometryPipeline.projectTo2D = function(geometry, attributeName, attributeName3D, attributeName2D, projection) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            if (!when.defined(attributeName)) {
                throw new Check.DeveloperError('attributeName is required.');
            }
            if (!when.defined(attributeName3D)) {
                throw new Check.DeveloperError('attributeName3D is required.');
            }
            if (!when.defined(attributeName2D)) {
                throw new Check.DeveloperError('attributeName2D is required.');
            }
            if (!when.defined(geometry.attributes[attributeName])) {
                throw new Check.DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.');
            }
            if (geometry.attributes[attributeName].componentDatatype !== ComponentDatatype.ComponentDatatype.DOUBLE) {
                throw new Check.DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.');
            }
            //>>includeEnd('debug');

            var attribute = geometry.attributes[attributeName];
            projection = (when.defined(projection)) ? projection : new Transforms.GeographicProjection();
            var ellipsoid = projection.ellipsoid;

            // Project original values to 2D.
            var values3D = attribute.values;
            var projectedValues = new Float64Array(values3D.length);
            var index = 0;

            for ( var i = 0; i < values3D.length; i += 3) {
                var value = Cartesian2.Cartesian3.fromArray(values3D, i, scratchProjectTo2DCartesian3);

                var lonLat = ellipsoid.cartesianToCartographic(value, scratchProjectTo2DCartographic);
                //>>includeStart('debug', pragmas.debug);
                if (!when.defined(lonLat)) {
                    throw new Check.DeveloperError('Could not project point (' + value.x + ', ' + value.y + ', ' + value.z + ') to 2D.');
                }
                //>>includeEnd('debug');

                var projectedLonLat = projection.project(lonLat, scratchProjectTo2DCartesian3);

                projectedValues[index++] = projectedLonLat.x;
                projectedValues[index++] = projectedLonLat.y;
                projectedValues[index++] = projectedLonLat.z;
            }

            // Rename original cartesians to WGS84 cartesians.
            geometry.attributes[attributeName3D] = attribute;

            // Replace original cartesians with 2D projected cartesians
            geometry.attributes[attributeName2D] = new GeometryAttribute.GeometryAttribute({
                componentDatatype : ComponentDatatype.ComponentDatatype.DOUBLE,
                componentsPerAttribute : 3,
                values : projectedValues
            });
            delete geometry.attributes[attributeName];

            return geometry;
        };

        var encodedResult = {
            high : 0.0,
            low : 0.0
        };

        /**
         * Encodes floating-point geometry attribute values as two separate attributes to improve
         * rendering precision.
         * <p>
         * This is commonly used to create high-precision position vertex attributes.
         * </p>
         *
         * @param {Geometry} geometry The geometry to modify.
         * @param {String} attributeName The name of the attribute.
         * @param {String} attributeHighName The name of the attribute for the encoded high bits.
         * @param {String} attributeLowName The name of the attribute for the encoded low bits.
         * @returns {Geometry} The modified <code>geometry</code> argument, with its encoded attribute.
         *
         * @exception {DeveloperError} geometry must have attribute matching the attributeName argument.
         * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE.
         *
         * @example
         * geometry = Cesium.GeometryPipeline.encodeAttribute(geometry, 'position3D', 'position3DHigh', 'position3DLow');
         */
        GeometryPipeline.encodeAttribute = function(geometry, attributeName, attributeHighName, attributeLowName) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            if (!when.defined(attributeName)) {
                throw new Check.DeveloperError('attributeName is required.');
            }
            if (!when.defined(attributeHighName)) {
                throw new Check.DeveloperError('attributeHighName is required.');
            }
            if (!when.defined(attributeLowName)) {
                throw new Check.DeveloperError('attributeLowName is required.');
            }
            if (!when.defined(geometry.attributes[attributeName])) {
                throw new Check.DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.');
            }
            if (geometry.attributes[attributeName].componentDatatype !== ComponentDatatype.ComponentDatatype.DOUBLE) {
                throw new Check.DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.');
            }
            //>>includeEnd('debug');

            var attribute = geometry.attributes[attributeName];
            var values = attribute.values;
            var length = values.length;
            var highValues = new Float32Array(length);
            var lowValues = new Float32Array(length);

            for (var i = 0; i < length; ++i) {
                EncodedCartesian3.EncodedCartesian3.encode(values[i], encodedResult);
                highValues[i] = encodedResult.high;
                lowValues[i] = encodedResult.low;
            }

            var componentsPerAttribute = attribute.componentsPerAttribute;

            geometry.attributes[attributeHighName] = new GeometryAttribute.GeometryAttribute({
                componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
                componentsPerAttribute : componentsPerAttribute,
                values : highValues
            });
            geometry.attributes[attributeLowName] = new GeometryAttribute.GeometryAttribute({
                componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
                componentsPerAttribute : componentsPerAttribute,
                values : lowValues
            });
            delete geometry.attributes[attributeName];

            return geometry;
        };

        var scratchCartesian3$1 = new Cartesian2.Cartesian3();

        function transformPoint(matrix, attribute) {
            if (when.defined(attribute)) {
                var values = attribute.values;
                var length = values.length;
                for (var i = 0; i < length; i += 3) {
                    Cartesian2.Cartesian3.unpack(values, i, scratchCartesian3$1);
                    Transforms.Matrix4.multiplyByPoint(matrix, scratchCartesian3$1, scratchCartesian3$1);
                    Cartesian2.Cartesian3.pack(scratchCartesian3$1, values, i);
                }
            }
        }

        function transformVector(matrix, attribute) {
            if (when.defined(attribute)) {
                var values = attribute.values;
                var length = values.length;
                for (var i = 0; i < length; i += 3) {
                    Cartesian2.Cartesian3.unpack(values, i, scratchCartesian3$1);
                    Transforms.Matrix3.multiplyByVector(matrix, scratchCartesian3$1, scratchCartesian3$1);
                    scratchCartesian3$1 = Cartesian2.Cartesian3.normalize(scratchCartesian3$1, scratchCartesian3$1);
                    Cartesian2.Cartesian3.pack(scratchCartesian3$1, values, i);
                }
            }
        }

        var inverseTranspose = new Transforms.Matrix4();
        var normalMatrix = new Transforms.Matrix3();

        /**
         * Transforms a geometry instance to world coordinates.  This changes
         * the instance's <code>modelMatrix</code> to {@link Matrix4.IDENTITY} and transforms the
         * following attributes if they are present: <code>position</code>, <code>normal</code>,
         * <code>tangent</code>, and <code>bitangent</code>.
         *
         * @param {GeometryInstance} instance The geometry instance to modify.
         * @returns {GeometryInstance} The modified <code>instance</code> argument, with its attributes transforms to world coordinates.
         *
         * @example
         * Cesium.GeometryPipeline.transformToWorldCoordinates(instance);
         */
        GeometryPipeline.transformToWorldCoordinates = function(instance) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(instance)) {
                throw new Check.DeveloperError('instance is required.');
            }
            //>>includeEnd('debug');

            var modelMatrix = instance.modelMatrix;

            if (Transforms.Matrix4.equals(modelMatrix, Transforms.Matrix4.IDENTITY)) {
                // Already in world coordinates
                return instance;
            }

            var attributes = instance.geometry.attributes;

            // Transform attributes in known vertex formats
            transformPoint(modelMatrix, attributes.position);
            transformPoint(modelMatrix, attributes.prevPosition);
            transformPoint(modelMatrix, attributes.nextPosition);

            if ((when.defined(attributes.normal)) ||
                (when.defined(attributes.tangent)) ||
                (when.defined(attributes.bitangent))) {

                Transforms.Matrix4.inverse(modelMatrix, inverseTranspose);
                Transforms.Matrix4.transpose(inverseTranspose, inverseTranspose);
                Transforms.Matrix4.getMatrix3(inverseTranspose, normalMatrix);

                transformVector(normalMatrix, attributes.normal);
                transformVector(normalMatrix, attributes.tangent);
                transformVector(normalMatrix, attributes.bitangent);
            }

            var boundingSphere = instance.geometry.boundingSphere;
            if (when.defined(boundingSphere)) {
                instance.geometry.boundingSphere = Transforms.BoundingSphere.transform(boundingSphere, modelMatrix, boundingSphere);
            }

            instance.modelMatrix = Transforms.Matrix4.clone(Transforms.Matrix4.IDENTITY);

            return instance;
        };

        function findAttributesInAllGeometries(instances, propertyName) {
            var length = instances.length;

            var attributesInAllGeometries = {};

            var attributes0 = instances[0][propertyName].attributes;
            var name;

            for (name in attributes0) {
                if (attributes0.hasOwnProperty(name) &&
                        when.defined(attributes0[name]) &&
                        when.defined(attributes0[name].values)) {

                    var attribute = attributes0[name];
                    var numberOfComponents = attribute.values.length;
                    var inAllGeometries = true;

                    // Does this same attribute exist in all geometries?
                    for (var i = 1; i < length; ++i) {
                        var otherAttribute = instances[i][propertyName].attributes[name];

                        if ((!when.defined(otherAttribute)) ||
                            (attribute.componentDatatype !== otherAttribute.componentDatatype) ||
                            (attribute.componentsPerAttribute !== otherAttribute.componentsPerAttribute) ||
                            (attribute.normalize !== otherAttribute.normalize)) {

                            inAllGeometries = false;
                            break;
                        }

                        numberOfComponents += otherAttribute.values.length;
                    }

                    if (inAllGeometries) {
                        attributesInAllGeometries[name] = new GeometryAttribute.GeometryAttribute({
                            componentDatatype : attribute.componentDatatype,
                            componentsPerAttribute : attribute.componentsPerAttribute,
                            normalize : attribute.normalize,
                            values : ComponentDatatype.ComponentDatatype.createTypedArray(attribute.componentDatatype, numberOfComponents)
                        });
                    }
                }
            }

            return attributesInAllGeometries;
        }

        var tempScratch = new Cartesian2.Cartesian3();

        function combineGeometries(instances, propertyName) {
            var length = instances.length;

            var name;
            var i;
            var j;
            var k;

            var m = instances[0].modelMatrix;
            var haveIndices = (when.defined(instances[0][propertyName].indices));
            var primitiveType = instances[0][propertyName].primitiveType;

            //>>includeStart('debug', pragmas.debug);
            for (i = 1; i < length; ++i) {
                if (!Transforms.Matrix4.equals(instances[i].modelMatrix, m)) {
                    throw new Check.DeveloperError('All instances must have the same modelMatrix.');
                }
                if ((when.defined(instances[i][propertyName].indices)) !== haveIndices) {
                    throw new Check.DeveloperError('All instance geometries must have an indices or not have one.');
                }
                if (instances[i][propertyName].primitiveType !== primitiveType) {
                    throw new Check.DeveloperError('All instance geometries must have the same primitiveType.');
                }
            }
            //>>includeEnd('debug');

            // Find subset of attributes in all geometries
            var attributes = findAttributesInAllGeometries(instances, propertyName);
            var values;
            var sourceValues;
            var sourceValuesLength;

            // Combine attributes from each geometry into a single typed array
            for (name in attributes) {
                if (attributes.hasOwnProperty(name)) {
                    values = attributes[name].values;

                    k = 0;
                    for (i = 0; i < length; ++i) {
                        sourceValues = instances[i][propertyName].attributes[name].values;
                        sourceValuesLength = sourceValues.length;

                        for (j = 0; j < sourceValuesLength; ++j) {
                            values[k++] = sourceValues[j];
                        }
                    }
                }
            }

            // Combine index lists
            var indices;

            if (haveIndices) {
                var numberOfIndices = 0;
                for (i = 0; i < length; ++i) {
                    numberOfIndices += instances[i][propertyName].indices.length;
                }

                var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(new GeometryAttribute.Geometry({
                    attributes : attributes,
                    primitiveType : GeometryAttribute.PrimitiveType.POINTS
                }));
                var destIndices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, numberOfIndices);

                var destOffset = 0;
                var offset = 0;

                for (i = 0; i < length; ++i) {
                    var sourceIndices = instances[i][propertyName].indices;
                    var sourceIndicesLen = sourceIndices.length;

                    for (k = 0; k < sourceIndicesLen; ++k) {
                        destIndices[destOffset++] = offset + sourceIndices[k];
                    }

                    offset += GeometryAttribute.Geometry.computeNumberOfVertices(instances[i][propertyName]);
                }

                indices = destIndices;
            }

            // Create bounding sphere that includes all instances
            var center = new Cartesian2.Cartesian3();
            var radius = 0.0;
            var bs;

            for (i = 0; i < length; ++i) {
                bs = instances[i][propertyName].boundingSphere;
                if (!when.defined(bs)) {
                    // If any geometries have an undefined bounding sphere, then so does the combined geometry
                    center = undefined;
                    break;
                }

                Cartesian2.Cartesian3.add(bs.center, center, center);
            }

            if (when.defined(center)) {
                Cartesian2.Cartesian3.divideByScalar(center, length, center);

                for (i = 0; i < length; ++i) {
                    bs = instances[i][propertyName].boundingSphere;
                    var tempRadius = Cartesian2.Cartesian3.magnitude(Cartesian2.Cartesian3.subtract(bs.center, center, tempScratch)) + bs.radius;

                    if (tempRadius > radius) {
                        radius = tempRadius;
                    }
                }
            }

            return new GeometryAttribute.Geometry({
                attributes : attributes,
                indices : indices,
                primitiveType : primitiveType,
                boundingSphere : (when.defined(center)) ? new Transforms.BoundingSphere(center, radius) : undefined
            });
        }

        /**
         * Combines geometry from several {@link GeometryInstance} objects into one geometry.
         * This concatenates the attributes, concatenates and adjusts the indices, and creates
         * a bounding sphere encompassing all instances.
         * <p>
         * If the instances do not have the same attributes, a subset of attributes common
         * to all instances is used, and the others are ignored.
         * </p>
         * <p>
         * This is used by {@link Primitive} to efficiently render a large amount of static data.
         * </p>
         *
         * @private
         *
         * @param {GeometryInstance[]} [instances] The array of {@link GeometryInstance} objects whose geometry will be combined.
         * @returns {Geometry} A single geometry created from the provided geometry instances.
         *
         * @exception {DeveloperError} All instances must have the same modelMatrix.
         * @exception {DeveloperError} All instance geometries must have an indices or not have one.
         * @exception {DeveloperError} All instance geometries must have the same primitiveType.
         *
         *
         * @example
         * for (var i = 0; i < instances.length; ++i) {
         *   Cesium.GeometryPipeline.transformToWorldCoordinates(instances[i]);
         * }
         * var geometries = Cesium.GeometryPipeline.combineInstances(instances);
         *
         * @see GeometryPipeline.transformToWorldCoordinates
         */
        GeometryPipeline.combineInstances = function(instances) {
            //>>includeStart('debug', pragmas.debug);
            if ((!when.defined(instances)) || (instances.length < 1)) {
                throw new Check.DeveloperError('instances is required and must have length greater than zero.');
            }
            //>>includeEnd('debug');

            var instanceGeometry = [];
            var instanceSplitGeometry = [];
            var length = instances.length;
            for (var i = 0; i < length; ++i) {
                var instance = instances[i];

                if (when.defined(instance.geometry)) {
                    instanceGeometry.push(instance);
                } else if (when.defined(instance.westHemisphereGeometry) && when.defined(instance.eastHemisphereGeometry)) {
                    instanceSplitGeometry.push(instance);
                }
            }

            var geometries = [];
            if (instanceGeometry.length > 0) {
                geometries.push(combineGeometries(instanceGeometry, 'geometry'));
            }

            if (instanceSplitGeometry.length > 0) {
                geometries.push(combineGeometries(instanceSplitGeometry, 'westHemisphereGeometry'));
                geometries.push(combineGeometries(instanceSplitGeometry, 'eastHemisphereGeometry'));
            }

            return geometries;
        };

        var normal = new Cartesian2.Cartesian3();
        var v0 = new Cartesian2.Cartesian3();
        var v1 = new Cartesian2.Cartesian3();
        var v2 = new Cartesian2.Cartesian3();

        /**
         * Computes per-vertex normals for a geometry containing <code>TRIANGLES</code> by averaging the normals of
         * all triangles incident to the vertex.  The result is a new <code>normal</code> attribute added to the geometry.
         * This assumes a counter-clockwise winding order.
         *
         * @param {Geometry} geometry The geometry to modify.
         * @returns {Geometry} The modified <code>geometry</code> argument with the computed <code>normal</code> attribute.
         *
         * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3.
         * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}.
         *
         * @example
         * Cesium.GeometryPipeline.computeNormal(geometry);
         */
        GeometryPipeline.computeNormal = function(geometry) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            if (!when.defined(geometry.attributes.position) || !when.defined(geometry.attributes.position.values)) {
                throw new Check.DeveloperError('geometry.attributes.position.values is required.');
            }
            if (!when.defined(geometry.indices)) {
                throw new Check.DeveloperError('geometry.indices is required.');
            }
            if (geometry.indices.length < 2 || geometry.indices.length % 3 !== 0) {
                throw new Check.DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.');
            }
            if (geometry.primitiveType !== GeometryAttribute.PrimitiveType.TRIANGLES) {
                throw new Check.DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.');
            }
            //>>includeEnd('debug');

            var indices = geometry.indices;
            var attributes = geometry.attributes;
            var vertices = attributes.position.values;
            var numVertices = attributes.position.values.length / 3;
            var numIndices = indices.length;
            var normalsPerVertex = new Array(numVertices);
            var normalsPerTriangle = new Array(numIndices / 3);
            var normalIndices = new Array(numIndices);
            var i;
            for ( i = 0; i < numVertices; i++) {
                normalsPerVertex[i] = {
                    indexOffset : 0,
                    count : 0,
                    currentCount : 0
                };
            }

            var j = 0;
            for (i = 0; i < numIndices; i += 3) {
                var i0 = indices[i];
                var i1 = indices[i + 1];
                var i2 = indices[i + 2];
                var i03 = i0 * 3;
                var i13 = i1 * 3;
                var i23 = i2 * 3;

                v0.x = vertices[i03];
                v0.y = vertices[i03 + 1];
                v0.z = vertices[i03 + 2];
                v1.x = vertices[i13];
                v1.y = vertices[i13 + 1];
                v1.z = vertices[i13 + 2];
                v2.x = vertices[i23];
                v2.y = vertices[i23 + 1];
                v2.z = vertices[i23 + 2];

                normalsPerVertex[i0].count++;
                normalsPerVertex[i1].count++;
                normalsPerVertex[i2].count++;

                Cartesian2.Cartesian3.subtract(v1, v0, v1);
                Cartesian2.Cartesian3.subtract(v2, v0, v2);
                normalsPerTriangle[j] = Cartesian2.Cartesian3.cross(v1, v2, new Cartesian2.Cartesian3());
                j++;
            }

            var indexOffset = 0;
            for (i = 0; i < numVertices; i++) {
                normalsPerVertex[i].indexOffset += indexOffset;
                indexOffset += normalsPerVertex[i].count;
            }

            j = 0;
            var vertexNormalData;
            for (i = 0; i < numIndices; i += 3) {
                vertexNormalData = normalsPerVertex[indices[i]];
                var index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
                normalIndices[index] = j;
                vertexNormalData.currentCount++;

                vertexNormalData = normalsPerVertex[indices[i + 1]];
                index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
                normalIndices[index] = j;
                vertexNormalData.currentCount++;

                vertexNormalData = normalsPerVertex[indices[i + 2]];
                index = vertexNormalData.indexOffset + vertexNormalData.currentCount;
                normalIndices[index] = j;
                vertexNormalData.currentCount++;

                j++;
            }

            var normalValues = new Float32Array(numVertices * 3);
            for (i = 0; i < numVertices; i++) {
                var i3 = i * 3;
                vertexNormalData = normalsPerVertex[i];
                Cartesian2.Cartesian3.clone(Cartesian2.Cartesian3.ZERO, normal);
                if (vertexNormalData.count > 0) {
                    for (j = 0; j < vertexNormalData.count; j++) {
                        Cartesian2.Cartesian3.add(normal, normalsPerTriangle[normalIndices[vertexNormalData.indexOffset + j]], normal);
                    }

                    // We can run into an issue where a vertex is used with 2 primitives that have opposite winding order.
                    if (Cartesian2.Cartesian3.equalsEpsilon(Cartesian2.Cartesian3.ZERO, normal, _Math.CesiumMath.EPSILON10)) {
                        Cartesian2.Cartesian3.clone(normalsPerTriangle[normalIndices[vertexNormalData.indexOffset]], normal);
                    }
                }

                // We end up with a zero vector probably because of a degenerate triangle
                if (Cartesian2.Cartesian3.equalsEpsilon(Cartesian2.Cartesian3.ZERO, normal, _Math.CesiumMath.EPSILON10)) {
                    // Default to (0,0,1)
                    normal.z = 1.0;
                }

                Cartesian2.Cartesian3.normalize(normal, normal);
                normalValues[i3] = normal.x;
                normalValues[i3 + 1] = normal.y;
                normalValues[i3 + 2] = normal.z;
            }

            geometry.attributes.normal = new GeometryAttribute.GeometryAttribute({
                componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
                componentsPerAttribute : 3,
                values : normalValues
            });

            return geometry;
        };

        var normalScratch = new Cartesian2.Cartesian3();
        var normalScale = new Cartesian2.Cartesian3();
        var tScratch = new Cartesian2.Cartesian3();

        /**
         * Computes per-vertex tangents and bitangents for a geometry containing <code>TRIANGLES</code>.
         * The result is new <code>tangent</code> and <code>bitangent</code> attributes added to the geometry.
         * This assumes a counter-clockwise winding order.
         * <p>
         * Based on <a href="http://www.terathon.com/code/tangent.html">Computing Tangent Space Basis Vectors
         * for an Arbitrary Mesh</a> by Eric Lengyel.
         * </p>
         *
         * @param {Geometry} geometry The geometry to modify.
         * @returns {Geometry} The modified <code>geometry</code> argument with the computed <code>tangent</code> and <code>bitangent</code> attributes.
         *
         * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3.
         * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}.
         *
         * @example
         * Cesium.GeometryPipeline.computeTangentAndBiTangent(geometry);
         */
        GeometryPipeline.computeTangentAndBitangent = function(geometry) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            //>>includeEnd('debug');

            var attributes = geometry.attributes;
            var indices = geometry.indices;

            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(attributes.position) || !when.defined(attributes.position.values)) {
                throw new Check.DeveloperError('geometry.attributes.position.values is required.');
            }
            if (!when.defined(attributes.normal) || !when.defined(attributes.normal.values)) {
                throw new Check.DeveloperError('geometry.attributes.normal.values is required.');
            }
            if (!when.defined(attributes.st) || !when.defined(attributes.st.values)) {
                throw new Check.DeveloperError('geometry.attributes.st.values is required.');
            }
            if (!when.defined(indices)) {
                throw new Check.DeveloperError('geometry.indices is required.');
            }
            if (indices.length < 2 || indices.length % 3 !== 0) {
                throw new Check.DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.');
            }
            if (geometry.primitiveType !== GeometryAttribute.PrimitiveType.TRIANGLES) {
                throw new Check.DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.');
            }
            //>>includeEnd('debug');

            var vertices = geometry.attributes.position.values;
            var normals = geometry.attributes.normal.values;
            var st = geometry.attributes.st.values;

            var numVertices = geometry.attributes.position.values.length / 3;
            var numIndices = indices.length;
            var tan1 = new Array(numVertices * 3);

            var i;
            for ( i = 0; i < tan1.length; i++) {
                tan1[i] = 0;
            }

            var i03;
            var i13;
            var i23;
            for (i = 0; i < numIndices; i += 3) {
                var i0 = indices[i];
                var i1 = indices[i + 1];
                var i2 = indices[i + 2];
                i03 = i0 * 3;
                i13 = i1 * 3;
                i23 = i2 * 3;
                var i02 = i0 * 2;
                var i12 = i1 * 2;
                var i22 = i2 * 2;

                var ux = vertices[i03];
                var uy = vertices[i03 + 1];
                var uz = vertices[i03 + 2];

                var wx = st[i02];
                var wy = st[i02 + 1];
                var t1 = st[i12 + 1] - wy;
                var t2 = st[i22 + 1] - wy;

                var r = 1.0 / ((st[i12] - wx) * t2 - (st[i22] - wx) * t1);
                var sdirx = (t2 * (vertices[i13] - ux) - t1 * (vertices[i23] - ux)) * r;
                var sdiry = (t2 * (vertices[i13 + 1] - uy) - t1 * (vertices[i23 + 1] - uy)) * r;
                var sdirz = (t2 * (vertices[i13 + 2] - uz) - t1 * (vertices[i23 + 2] - uz)) * r;

                tan1[i03] += sdirx;
                tan1[i03 + 1] += sdiry;
                tan1[i03 + 2] += sdirz;

                tan1[i13] += sdirx;
                tan1[i13 + 1] += sdiry;
                tan1[i13 + 2] += sdirz;

                tan1[i23] += sdirx;
                tan1[i23 + 1] += sdiry;
                tan1[i23 + 2] += sdirz;
            }

            var tangentValues = new Float32Array(numVertices * 3);
            var bitangentValues = new Float32Array(numVertices * 3);

            for (i = 0; i < numVertices; i++) {
                i03 = i * 3;
                i13 = i03 + 1;
                i23 = i03 + 2;

                var n = Cartesian2.Cartesian3.fromArray(normals, i03, normalScratch);
                var t = Cartesian2.Cartesian3.fromArray(tan1, i03, tScratch);
                var scalar = Cartesian2.Cartesian3.dot(n, t);
                Cartesian2.Cartesian3.multiplyByScalar(n, scalar, normalScale);
                Cartesian2.Cartesian3.normalize(Cartesian2.Cartesian3.subtract(t, normalScale, t), t);

                tangentValues[i03] = t.x;
                tangentValues[i13] = t.y;
                tangentValues[i23] = t.z;

                Cartesian2.Cartesian3.normalize(Cartesian2.Cartesian3.cross(n, t, t), t);

                bitangentValues[i03] = t.x;
                bitangentValues[i13] = t.y;
                bitangentValues[i23] = t.z;
            }

            geometry.attributes.tangent = new GeometryAttribute.GeometryAttribute({
                componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
                componentsPerAttribute : 3,
                values : tangentValues
            });

            geometry.attributes.bitangent = new GeometryAttribute.GeometryAttribute({
                componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
                componentsPerAttribute : 3,
                values : bitangentValues
            });

            return geometry;
        };

        var scratchCartesian2$1 = new Cartesian2.Cartesian2();
        var toEncode1 = new Cartesian2.Cartesian3();
        var toEncode2 = new Cartesian2.Cartesian3();
        var toEncode3 = new Cartesian2.Cartesian3();
        var encodeResult2 = new Cartesian2.Cartesian2();
        /**
         * Compresses and packs geometry normal attribute values to save memory.
         *
         * @param {Geometry} geometry The geometry to modify.
         * @returns {Geometry} The modified <code>geometry</code> argument, with its normals compressed and packed.
         *
         * @example
         * geometry = Cesium.GeometryPipeline.compressVertices(geometry);
         */
        GeometryPipeline.compressVertices = function(geometry) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(geometry)) {
                throw new Check.DeveloperError('geometry is required.');
            }
            //>>includeEnd('debug');

            var extrudeAttribute = geometry.attributes.extrudeDirection;
            var i;
            var numVertices;
            if (when.defined(extrudeAttribute)) {
                //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes
                var extrudeDirections = extrudeAttribute.values;
                numVertices = extrudeDirections.length / 3.0;
                var compressedDirections = new Float32Array(numVertices * 2);

                var i2 = 0;
                for (i = 0; i < numVertices; ++i) {
                    Cartesian2.Cartesian3.fromArray(extrudeDirections, i * 3.0, toEncode1);
                    if (Cartesian2.Cartesian3.equals(toEncode1, Cartesian2.Cartesian3.ZERO)) {
                        i2 += 2;
                        continue;
                    }
                    encodeResult2 = AttributeCompression.AttributeCompression.octEncodeInRange(toEncode1, 65535, encodeResult2);
                    compressedDirections[i2++] = encodeResult2.x;
                    compressedDirections[i2++] = encodeResult2.y;
                }

                geometry.attributes.compressedAttributes = new GeometryAttribute.GeometryAttribute({
                    componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
                    componentsPerAttribute : 2,
                    values : compressedDirections
                });
                delete geometry.attributes.extrudeDirection;
                return geometry;
            }

            var normalAttribute = geometry.attributes.normal;
            var stAttribute = geometry.attributes.st;

            var hasNormal = when.defined(normalAttribute);
            var hasSt = when.defined(stAttribute);
            if (!hasNormal && !hasSt) {
                return geometry;
            }

            var tangentAttribute = geometry.attributes.tangent;
            var bitangentAttribute = geometry.attributes.bitangent;

            var hasTangent = when.defined(tangentAttribute);
            var hasBitangent = when.defined(bitangentAttribute);

            var normals;
            var st;
            var tangents;
            var bitangents;

            if (hasNormal) {
                normals = normalAttribute.values;
            }
            if (hasSt) {
                st = stAttribute.values;
            }
            if (hasTangent) {
                tangents = tangentAttribute.values;
            }
            if (hasBitangent) {
                bitangents = bitangentAttribute.values;
            }

            var length = hasNormal ? normals.length : st.length;
            var numComponents = hasNormal ? 3.0 : 2.0;
            numVertices = length / numComponents;

            var compressedLength = numVertices;
            var numCompressedComponents = hasSt && hasNormal ? 2.0 : 1.0;
            numCompressedComponents += hasTangent || hasBitangent ? 1.0 : 0.0;
            compressedLength *= numCompressedComponents;

            var compressedAttributes = new Float32Array(compressedLength);

            var normalIndex = 0;
            for (i = 0; i < numVertices; ++i) {
                if (hasSt) {
                    Cartesian2.Cartesian2.fromArray(st, i * 2.0, scratchCartesian2$1);
                    compressedAttributes[normalIndex++] = AttributeCompression.AttributeCompression.compressTextureCoordinates(scratchCartesian2$1);
                }

                var index = i * 3.0;
                if (hasNormal && when.defined(tangents) && when.defined(bitangents)) {
                    Cartesian2.Cartesian3.fromArray(normals, index, toEncode1);
                    Cartesian2.Cartesian3.fromArray(tangents, index, toEncode2);
                    Cartesian2.Cartesian3.fromArray(bitangents, index, toEncode3);

                    AttributeCompression.AttributeCompression.octPack(toEncode1, toEncode2, toEncode3, scratchCartesian2$1);
                    compressedAttributes[normalIndex++] = scratchCartesian2$1.x;
                    compressedAttributes[normalIndex++] = scratchCartesian2$1.y;
                } else {
                    if (hasNormal) {
                        Cartesian2.Cartesian3.fromArray(normals, index, toEncode1);
                        compressedAttributes[normalIndex++] = AttributeCompression.AttributeCompression.octEncodeFloat(toEncode1);
                    }

                    if (hasTangent) {
                        Cartesian2.Cartesian3.fromArray(tangents, index, toEncode1);
                        compressedAttributes[normalIndex++] = AttributeCompression.AttributeCompression.octEncodeFloat(toEncode1);
                    }

                    if (hasBitangent) {
                        Cartesian2.Cartesian3.fromArray(bitangents, index, toEncode1);
                        compressedAttributes[normalIndex++] = AttributeCompression.AttributeCompression.octEncodeFloat(toEncode1);
                    }
                }
            }

            geometry.attributes.compressedAttributes = new GeometryAttribute.GeometryAttribute({
                componentDatatype : ComponentDatatype.ComponentDatatype.FLOAT,
                componentsPerAttribute : numCompressedComponents,
                values : compressedAttributes
            });

            if (hasNormal) {
                delete geometry.attributes.normal;
            }
            if (hasSt) {
                delete geometry.attributes.st;
            }
            if (hasBitangent) {
                delete geometry.attributes.bitangent;
            }
            if (hasTangent) {
                delete geometry.attributes.tangent;
            }

            return geometry;
        };

        function indexTriangles(geometry) {
            if (when.defined(geometry.indices)) {
                return geometry;
            }
            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);

            //>>includeStart('debug', pragmas.debug);
            if (numberOfVertices < 3) {
                throw new Check.DeveloperError('The number of vertices must be at least three.');
            }
            if (numberOfVertices % 3 !== 0) {
                throw new Check.DeveloperError('The number of vertices must be a multiple of three.');
            }
            //>>includeEnd('debug');

            var indices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices);
            for (var i = 0; i < numberOfVertices; ++i) {
                indices[i] = i;
            }

            geometry.indices = indices;
            return geometry;
        }

        function indexTriangleFan(geometry) {
            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);

            //>>includeStart('debug', pragmas.debug);
            if (numberOfVertices < 3) {
                throw new Check.DeveloperError('The number of vertices must be at least three.');
            }
            //>>includeEnd('debug');

            var indices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3);
            indices[0] = 1;
            indices[1] = 0;
            indices[2] = 2;

            var indicesIndex = 3;
            for (var i = 3; i < numberOfVertices; ++i) {
                indices[indicesIndex++] = i - 1;
                indices[indicesIndex++] = 0;
                indices[indicesIndex++] = i;
            }

            geometry.indices = indices;
            geometry.primitiveType = GeometryAttribute.PrimitiveType.TRIANGLES;
            return geometry;
        }

        function indexTriangleStrip(geometry) {
            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);

            //>>includeStart('debug', pragmas.debug);
            if (numberOfVertices < 3) {
                throw new Check.DeveloperError('The number of vertices must be at least 3.');
            }
            //>>includeEnd('debug');

            var indices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3);
            indices[0] = 0;
            indices[1] = 1;
            indices[2] = 2;

            if (numberOfVertices > 3) {
                indices[3] = 0;
                indices[4] = 2;
                indices[5] = 3;
            }

            var indicesIndex = 6;
            for (var i = 3; i < numberOfVertices - 1; i += 2) {
                indices[indicesIndex++] = i;
                indices[indicesIndex++] = i - 1;
                indices[indicesIndex++] = i + 1;

                if (i + 2 < numberOfVertices) {
                    indices[indicesIndex++] = i;
                    indices[indicesIndex++] = i + 1;
                    indices[indicesIndex++] = i + 2;
                }
            }

            geometry.indices = indices;
            geometry.primitiveType = GeometryAttribute.PrimitiveType.TRIANGLES;
            return geometry;
        }

        function indexLines(geometry) {
            if (when.defined(geometry.indices)) {
                return geometry;
            }
            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);

            //>>includeStart('debug', pragmas.debug);
            if (numberOfVertices < 2) {
                throw new Check.DeveloperError('The number of vertices must be at least two.');
            }
            if (numberOfVertices % 2 !== 0) {
                throw new Check.DeveloperError('The number of vertices must be a multiple of 2.');
            }
            //>>includeEnd('debug');

            var indices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices);
            for (var i = 0; i < numberOfVertices; ++i) {
                indices[i] = i;
            }

            geometry.indices = indices;
            return geometry;
        }

        function indexLineStrip(geometry) {
            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);

            //>>includeStart('debug', pragmas.debug);
            if (numberOfVertices < 2) {
                throw new Check.DeveloperError('The number of vertices must be at least two.');
            }
            //>>includeEnd('debug');

            var indices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 1) * 2);
            indices[0] = 0;
            indices[1] = 1;
            var indicesIndex = 2;
            for (var i = 2; i < numberOfVertices; ++i) {
                indices[indicesIndex++] = i - 1;
                indices[indicesIndex++] = i;
            }

            geometry.indices = indices;
            geometry.primitiveType = GeometryAttribute.PrimitiveType.LINES;
            return geometry;
        }

        function indexLineLoop(geometry) {
            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);

            //>>includeStart('debug', pragmas.debug);
            if (numberOfVertices < 2) {
                throw new Check.DeveloperError('The number of vertices must be at least two.');
            }
            //>>includeEnd('debug');

            var indices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices * 2);

            indices[0] = 0;
            indices[1] = 1;

            var indicesIndex = 2;
            for (var i = 2; i < numberOfVertices; ++i) {
                indices[indicesIndex++] = i - 1;
                indices[indicesIndex++] = i;
            }

            indices[indicesIndex++] = numberOfVertices - 1;
            indices[indicesIndex] = 0;

            geometry.indices = indices;
            geometry.primitiveType = GeometryAttribute.PrimitiveType.LINES;
            return geometry;
        }

        function indexPrimitive(geometry) {
            switch (geometry.primitiveType) {
            case GeometryAttribute.PrimitiveType.TRIANGLE_FAN:
                return indexTriangleFan(geometry);
            case GeometryAttribute.PrimitiveType.TRIANGLE_STRIP:
                return indexTriangleStrip(geometry);
            case GeometryAttribute.PrimitiveType.TRIANGLES:
                return indexTriangles(geometry);
            case GeometryAttribute.PrimitiveType.LINE_STRIP:
                return indexLineStrip(geometry);
            case GeometryAttribute.PrimitiveType.LINE_LOOP:
                return indexLineLoop(geometry);
            case GeometryAttribute.PrimitiveType.LINES:
                return indexLines(geometry);
            }

            return geometry;
        }

        function offsetPointFromXZPlane(p, isBehind) {
            if (Math.abs(p.y) < _Math.CesiumMath.EPSILON6){
                if (isBehind) {
                    p.y = -_Math.CesiumMath.EPSILON6;
                } else {
                    p.y = _Math.CesiumMath.EPSILON6;
                }
            }
        }

        function offsetTriangleFromXZPlane(p0, p1, p2) {
            if (p0.y !== 0.0 && p1.y !== 0.0 && p2.y !== 0.0) {
                offsetPointFromXZPlane(p0, p0.y < 0.0);
                offsetPointFromXZPlane(p1, p1.y < 0.0);
                offsetPointFromXZPlane(p2, p2.y < 0.0);
                return;
            }

            var p0y = Math.abs(p0.y);
            var p1y = Math.abs(p1.y);
            var p2y = Math.abs(p2.y);

            var sign;
            if (p0y > p1y) {
                if (p0y > p2y) {
                    sign = _Math.CesiumMath.sign(p0.y);
                } else {
                    sign = _Math.CesiumMath.sign(p2.y);
                }
            } else if (p1y > p2y) {
                sign = _Math.CesiumMath.sign(p1.y);
            } else {
                sign = _Math.CesiumMath.sign(p2.y);
            }

            var isBehind = sign < 0.0;
            offsetPointFromXZPlane(p0, isBehind);
            offsetPointFromXZPlane(p1, isBehind);
            offsetPointFromXZPlane(p2, isBehind);
        }

        var c3 = new Cartesian2.Cartesian3();
        function getXZIntersectionOffsetPoints(p, p1, u1, v1) {
            Cartesian2.Cartesian3.add(p, Cartesian2.Cartesian3.multiplyByScalar(Cartesian2.Cartesian3.subtract(p1, p, c3), p.y/(p.y-p1.y), c3), u1);
            Cartesian2.Cartesian3.clone(u1, v1);
            offsetPointFromXZPlane(u1, true);
            offsetPointFromXZPlane(v1, false);
        }

        var u1 = new Cartesian2.Cartesian3();
        var u2 = new Cartesian2.Cartesian3();
        var q1 = new Cartesian2.Cartesian3();
        var q2 = new Cartesian2.Cartesian3();

        var splitTriangleResult = {
            positions : new Array(7),
            indices : new Array(3 * 3)
        };

        function splitTriangle(p0, p1, p2) {
            // In WGS84 coordinates, for a triangle approximately on the
            // ellipsoid to cross the IDL, first it needs to be on the
            // negative side of the plane x = 0.
            if ((p0.x >= 0.0) || (p1.x >= 0.0) || (p2.x >= 0.0)) {
                return undefined;
            }

            offsetTriangleFromXZPlane(p0, p1, p2);

            var p0Behind = p0.y < 0.0;
            var p1Behind = p1.y < 0.0;
            var p2Behind = p2.y < 0.0;

            var numBehind = 0;
            numBehind += p0Behind ? 1 : 0;
            numBehind += p1Behind ? 1 : 0;
            numBehind += p2Behind ? 1 : 0;

            var indices = splitTriangleResult.indices;

            if (numBehind === 1) {
                indices[1] = 3;
                indices[2] = 4;
                indices[5] = 6;
                indices[7] = 6;
                indices[8] = 5;

                if (p0Behind) {
                    getXZIntersectionOffsetPoints(p0, p1, u1, q1);
                    getXZIntersectionOffsetPoints(p0, p2, u2, q2);

                    indices[0] = 0;
                    indices[3] = 1;
                    indices[4] = 2;
                    indices[6] = 1;
                } else if (p1Behind) {
                    getXZIntersectionOffsetPoints(p1, p2, u1, q1);
                    getXZIntersectionOffsetPoints(p1, p0, u2, q2);

                    indices[0] = 1;
                    indices[3] = 2;
                    indices[4] = 0;
                    indices[6] = 2;
                } else if (p2Behind) {
                    getXZIntersectionOffsetPoints(p2, p0, u1, q1);
                    getXZIntersectionOffsetPoints(p2, p1, u2, q2);

                    indices[0] = 2;
                    indices[3] = 0;
                    indices[4] = 1;
                    indices[6] = 0;
                }
            } else if (numBehind === 2) {
                indices[2] = 4;
                indices[4] = 4;
                indices[5] = 3;
                indices[7] = 5;
                indices[8] = 6;

                if (!p0Behind) {
                    getXZIntersectionOffsetPoints(p0, p1, u1, q1);
                    getXZIntersectionOffsetPoints(p0, p2, u2, q2);

                    indices[0] = 1;
                    indices[1] = 2;
                    indices[3] = 1;
                    indices[6] = 0;
                } else if (!p1Behind) {
                    getXZIntersectionOffsetPoints(p1, p2, u1, q1);
                    getXZIntersectionOffsetPoints(p1, p0, u2, q2);

                    indices[0] = 2;
                    indices[1] = 0;
                    indices[3] = 2;
                    indices[6] = 1;
                } else if (!p2Behind) {
                    getXZIntersectionOffsetPoints(p2, p0, u1, q1);
                    getXZIntersectionOffsetPoints(p2, p1, u2, q2);

                    indices[0] = 0;
                    indices[1] = 1;
                    indices[3] = 0;
                    indices[6] = 2;
                }
            }

            var positions = splitTriangleResult.positions;
            positions[0] = p0;
            positions[1] = p1;
            positions[2] = p2;
            positions.length = 3;

            if (numBehind === 1 || numBehind === 2) {
                positions[3] = u1;
                positions[4] = u2;
                positions[5] = q1;
                positions[6] = q2;
                positions.length = 7;
            }

            return splitTriangleResult;
        }

        function updateGeometryAfterSplit(geometry, computeBoundingSphere) {
            var attributes = geometry.attributes;

            if (attributes.position.values.length === 0) {
                return undefined;
            }

            for (var property in attributes) {
                if (attributes.hasOwnProperty(property) &&
                        when.defined(attributes[property]) &&
                        when.defined(attributes[property].values)) {

                    var attribute = attributes[property];
                    attribute.values = ComponentDatatype.ComponentDatatype.createTypedArray(attribute.componentDatatype, attribute.values);
                }
            }

            var numberOfVertices = GeometryAttribute.Geometry.computeNumberOfVertices(geometry);
            geometry.indices = IndexDatatype.IndexDatatype.createTypedArray(numberOfVertices, geometry.indices);

            if (computeBoundingSphere) {
                geometry.boundingSphere = Transforms.BoundingSphere.fromVertices(attributes.position.values);
            }

            return geometry;
        }

        function copyGeometryForSplit(geometry) {
            var attributes = geometry.attributes;
            var copiedAttributes = {};

            for (var property in attributes) {
                if (attributes.hasOwnProperty(property) &&
                        when.defined(attributes[property]) &&
                        when.defined(attributes[property].values)) {

                    var attribute = attributes[property];
                    copiedAttributes[property] = new GeometryAttribute.GeometryAttribute({
                        componentDatatype : attribute.componentDatatype,
                        componentsPerAttribute : attribute.componentsPerAttribute,
                        normalize : attribute.normalize,
                        values : []
                    });
                }
            }

            return new GeometryAttribute.Geometry({
                attributes : copiedAttributes,
                indices : [],
                primitiveType : geometry.primitiveType
            });
        }

        function updateInstanceAfterSplit(instance, westGeometry, eastGeometry) {
            var computeBoundingSphere = when.defined(instance.geometry.boundingSphere);

            westGeometry = updateGeometryAfterSplit(westGeometry, computeBoundingSphere);
            eastGeometry = updateGeometryAfterSplit(eastGeometry, computeBoundingSphere);

            if (when.defined(eastGeometry) && !when.defined(westGeometry)) {
                instance.geometry = eastGeometry;
            } else if (!when.defined(eastGeometry) && when.defined(westGeometry)) {
                instance.geometry = westGeometry;
            } else {
                instance.westHemisphereGeometry = westGeometry;
                instance.eastHemisphereGeometry = eastGeometry;
                instance.geometry = undefined;
            }
        }

        function generateBarycentricInterpolateFunction(CartesianType, numberOfComponents) {
            var v0Scratch = new CartesianType();
            var v1Scratch = new CartesianType();
            var v2Scratch = new CartesianType();

            return function(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, normalize) {
                var v0 = CartesianType.fromArray(sourceValues, i0 * numberOfComponents, v0Scratch);
                var v1 = CartesianType.fromArray(sourceValues, i1 * numberOfComponents, v1Scratch);
                var v2 = CartesianType.fromArray(sourceValues, i2 * numberOfComponents, v2Scratch);

                CartesianType.multiplyByScalar(v0, coords.x, v0);
                CartesianType.multiplyByScalar(v1, coords.y, v1);
                CartesianType.multiplyByScalar(v2, coords.z, v2);

                var value = CartesianType.add(v0, v1, v0);
                CartesianType.add(value, v2, value);

                if (normalize) {
                    CartesianType.normalize(value, value);
                }

                CartesianType.pack(value, currentValues, insertedIndex * numberOfComponents);
            };
        }

        var interpolateAndPackCartesian4 = generateBarycentricInterpolateFunction(Transforms.Cartesian4, 4);
        var interpolateAndPackCartesian3 = generateBarycentricInterpolateFunction(Cartesian2.Cartesian3, 3);
        var interpolateAndPackCartesian2 = generateBarycentricInterpolateFunction(Cartesian2.Cartesian2, 2);
        var interpolateAndPackBoolean = function(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex) {
            var v1 = sourceValues[i0] * coords.x;
            var v2 = sourceValues[i1] * coords.y;
            var v3 = sourceValues[i2] * coords.z;
            currentValues[insertedIndex] = (v1 + v2 + v3) > _Math.CesiumMath.EPSILON6 ? 1 : 0;
        };

        var p0Scratch = new Cartesian2.Cartesian3();
        var p1Scratch = new Cartesian2.Cartesian3();
        var p2Scratch = new Cartesian2.Cartesian3();
        var barycentricScratch = new Cartesian2.Cartesian3();

        function computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, allAttributes, insertedIndex) {
            if (!when.defined(normals) && !when.defined(tangents) && !when.defined(bitangents) && !when.defined(texCoords) && !when.defined(extrudeDirections) && customAttributesLength === 0) {
                return;
            }

            var p0 = Cartesian2.Cartesian3.fromArray(positions, i0 * 3, p0Scratch);
            var p1 = Cartesian2.Cartesian3.fromArray(positions, i1 * 3, p1Scratch);
            var p2 = Cartesian2.Cartesian3.fromArray(positions, i2 * 3, p2Scratch);
            var coords = barycentricCoordinates(point, p0, p1, p2, barycentricScratch);

            if (when.defined(normals)) {
                interpolateAndPackCartesian3(i0, i1, i2, coords, normals, currentAttributes.normal.values, insertedIndex, true);
            }

            if (when.defined(extrudeDirections)) {
                var d0 = Cartesian2.Cartesian3.fromArray(extrudeDirections, i0 * 3, p0Scratch);
                var d1 = Cartesian2.Cartesian3.fromArray(extrudeDirections, i1 * 3, p1Scratch);
                var d2 = Cartesian2.Cartesian3.fromArray(extrudeDirections, i2 * 3, p2Scratch);

                Cartesian2.Cartesian3.multiplyByScalar(d0, coords.x, d0);
                Cartesian2.Cartesian3.multiplyByScalar(d1, coords.y, d1);
                Cartesian2.Cartesian3.multiplyByScalar(d2, coords.z, d2);

                var direction;
                if (!Cartesian2.Cartesian3.equals(d0, Cartesian2.Cartesian3.ZERO) || !Cartesian2.Cartesian3.equals(d1, Cartesian2.Cartesian3.ZERO) || !Cartesian2.Cartesian3.equals(d2, Cartesian2.Cartesian3.ZERO)) {
                    direction = Cartesian2.Cartesian3.add(d0, d1, d0);
                    Cartesian2.Cartesian3.add(direction, d2, direction);
                    Cartesian2.Cartesian3.normalize(direction, direction);
                } else {
                    direction = p0Scratch;
                    direction.x = 0;
                    direction.y = 0;
                    direction.z = 0;
                }
                Cartesian2.Cartesian3.pack(direction, currentAttributes.extrudeDirection.values, insertedIndex * 3);
            }

            if (when.defined(applyOffset)) {
                interpolateAndPackBoolean(i0, i1, i2, coords, applyOffset, currentAttributes.applyOffset.values, insertedIndex);
            }

            if (when.defined(tangents)) {
                interpolateAndPackCartesian3(i0, i1, i2, coords, tangents, currentAttributes.tangent.values, insertedIndex, true);
            }

            if (when.defined(bitangents)) {
                interpolateAndPackCartesian3(i0, i1, i2, coords, bitangents, currentAttributes.bitangent.values, insertedIndex, true);
            }

            if (when.defined(texCoords)) {
                interpolateAndPackCartesian2(i0, i1, i2, coords, texCoords, currentAttributes.st.values, insertedIndex);
            }

            if (customAttributesLength > 0) {
                for (var i = 0; i < customAttributesLength; i++) {
                    var attributeName = customAttributeNames[i];
                    genericInterpolate(i0, i1, i2, coords, insertedIndex, allAttributes[attributeName], currentAttributes[attributeName]);
                }
            }
        }

        function genericInterpolate(i0, i1, i2, coords, insertedIndex, sourceAttribute, currentAttribute) {
            var componentsPerAttribute = sourceAttribute.componentsPerAttribute;
            var sourceValues = sourceAttribute.values;
            var currentValues = currentAttribute.values;
            switch(componentsPerAttribute) {
                case 4:
                    interpolateAndPackCartesian4(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false);
                    break;
                case 3:
                    interpolateAndPackCartesian3(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false);
                    break;
                case 2:
                    interpolateAndPackCartesian2(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false);
                    break;
                default:
                    currentValues[insertedIndex] = sourceValues[i0] * coords.x + sourceValues[i1] * coords.y + sourceValues[i2] * coords.z;
            }
        }

        function insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, currentIndex, point) {
            var insertIndex = currentAttributes.position.values.length / 3;

            if (currentIndex !== -1) {
                var prevIndex = indices[currentIndex];
                var newIndex = currentIndexMap[prevIndex];

                if (newIndex === -1) {
                    currentIndexMap[prevIndex] = insertIndex;
                    currentAttributes.position.values.push(point.x, point.y, point.z);
                    currentIndices.push(insertIndex);
                    return insertIndex;
                }

                currentIndices.push(newIndex);
                return newIndex;
            }

            currentAttributes.position.values.push(point.x, point.y, point.z);
            currentIndices.push(insertIndex);
            return insertIndex;
        }

        var NAMED_ATTRIBUTES = {
            position : true,
            normal : true,
            bitangent : true,
            tangent : true,
            st : true,
            extrudeDirection : true,
            applyOffset: true
        };
        function splitLongitudeTriangles(instance) {
            var geometry = instance.geometry;
            var attributes = geometry.attributes;
            var positions = attributes.position.values;
            var normals = (when.defined(attributes.normal)) ? attributes.normal.values : undefined;
            var bitangents = (when.defined(attributes.bitangent)) ? attributes.bitangent.values : undefined;
            var tangents = (when.defined(attributes.tangent)) ? attributes.tangent.values : undefined;
            var texCoords = (when.defined(attributes.st)) ? attributes.st.values : undefined;
            var extrudeDirections = (when.defined(attributes.extrudeDirection)) ? attributes.extrudeDirection.values : undefined;
            var applyOffset = when.defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined;
            var indices = geometry.indices;

            var customAttributeNames = [];
            for (var attributeName in attributes) {
                if (attributes.hasOwnProperty(attributeName) && !NAMED_ATTRIBUTES[attributeName] && when.defined(attributes[attributeName])) {
                    customAttributeNames.push(attributeName);
                }
            }
            var customAttributesLength = customAttributeNames.length;

            var eastGeometry = copyGeometryForSplit(geometry);
            var westGeometry = copyGeometryForSplit(geometry);

            var currentAttributes;
            var currentIndices;
            var currentIndexMap;
            var insertedIndex;
            var i;

            var westGeometryIndexMap = [];
            westGeometryIndexMap.length = positions.length / 3;

            var eastGeometryIndexMap = [];
            eastGeometryIndexMap.length = positions.length / 3;

            for (i = 0; i < westGeometryIndexMap.length; ++i) {
                westGeometryIndexMap[i] = -1;
                eastGeometryIndexMap[i] = -1;
            }

            var len = indices.length;
            for (i = 0; i < len; i += 3) {
                var i0 = indices[i];
                var i1 = indices[i + 1];
                var i2 = indices[i + 2];

                var p0 = Cartesian2.Cartesian3.fromArray(positions, i0 * 3);
                var p1 = Cartesian2.Cartesian3.fromArray(positions, i1 * 3);
                var p2 = Cartesian2.Cartesian3.fromArray(positions, i2 * 3);

                var result = splitTriangle(p0, p1, p2);
                if (when.defined(result) && result.positions.length > 3) {
                    var resultPositions = result.positions;
                    var resultIndices = result.indices;
                    var resultLength = resultIndices.length;

                    for (var j = 0; j < resultLength; ++j) {
                        var resultIndex = resultIndices[j];
                        var point = resultPositions[resultIndex];

                        if (point.y < 0.0) {
                            currentAttributes = westGeometry.attributes;
                            currentIndices = westGeometry.indices;
                            currentIndexMap = westGeometryIndexMap;
                        } else {
                            currentAttributes = eastGeometry.attributes;
                            currentIndices = eastGeometry.indices;
                            currentIndexMap = eastGeometryIndexMap;
                        }

                        insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, resultIndex < 3 ? i + resultIndex : -1, point);
                        computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);
                    }
                } else {
                    if (when.defined(result)) {
                        p0 = result.positions[0];
                        p1 = result.positions[1];
                        p2 = result.positions[2];
                    }

                    if (p0.y < 0.0) {
                        currentAttributes = westGeometry.attributes;
                        currentIndices = westGeometry.indices;
                        currentIndexMap = westGeometryIndexMap;
                    } else {
                        currentAttributes = eastGeometry.attributes;
                        currentIndices = eastGeometry.indices;
                        currentIndexMap = eastGeometryIndexMap;
                    }

                    insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i, p0);
                    computeTriangleAttributes(i0, i1, i2, p0, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);

                    insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1);
                    computeTriangleAttributes(i0, i1, i2, p1, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);

                    insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 2, p2);
                    computeTriangleAttributes(i0, i1, i2, p2, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex);
                }
            }

            updateInstanceAfterSplit(instance, westGeometry, eastGeometry);
        }

        var xzPlane = Plane.Plane.fromPointNormal(Cartesian2.Cartesian3.ZERO, Cartesian2.Cartesian3.UNIT_Y);

        var offsetScratch = new Cartesian2.Cartesian3();
        var offsetPointScratch = new Cartesian2.Cartesian3();

        function computeLineAttributes(i0, i1, point, positions, insertIndex, currentAttributes, applyOffset) {
            if (!when.defined(applyOffset)) {
                return;
            }

            var p0 = Cartesian2.Cartesian3.fromArray(positions, i0 * 3, p0Scratch);
            if (Cartesian2.Cartesian3.equalsEpsilon(p0, point, _Math.CesiumMath.EPSILON10)) {
                currentAttributes.applyOffset.values[insertIndex] = applyOffset[i0];
            } else {
                currentAttributes.applyOffset.values[insertIndex] = applyOffset[i1];
            }

        }

        function splitLongitudeLines(instance) {
            var geometry = instance.geometry;
            var attributes = geometry.attributes;
            var positions = attributes.position.values;
            var applyOffset = when.defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined;
            var indices = geometry.indices;

            var eastGeometry = copyGeometryForSplit(geometry);
            var westGeometry = copyGeometryForSplit(geometry);

            var i;
            var length = indices.length;

            var westGeometryIndexMap = [];
            westGeometryIndexMap.length = positions.length / 3;

            var eastGeometryIndexMap = [];
            eastGeometryIndexMap.length = positions.length / 3;

            for (i = 0; i < westGeometryIndexMap.length; ++i) {
                westGeometryIndexMap[i] = -1;
                eastGeometryIndexMap[i] = -1;
            }

            for (i = 0; i < length; i += 2) {
                var i0 = indices[i];
                var i1 = indices[i + 1];

                var p0 = Cartesian2.Cartesian3.fromArray(positions, i0 * 3, p0Scratch);
                var p1 = Cartesian2.Cartesian3.fromArray(positions, i1 * 3, p1Scratch);
                var insertIndex;

                if (Math.abs(p0.y) < _Math.CesiumMath.EPSILON6){
                    if (p0.y < 0.0) {
                        p0.y = -_Math.CesiumMath.EPSILON6;
                    } else {
                        p0.y = _Math.CesiumMath.EPSILON6;
                    }
                }

                if (Math.abs(p1.y) < _Math.CesiumMath.EPSILON6){
                    if (p1.y < 0.0) {
                        p1.y = -_Math.CesiumMath.EPSILON6;
                    } else {
                        p1.y = _Math.CesiumMath.EPSILON6;
                    }
                }

                var p0Attributes = eastGeometry.attributes;
                var p0Indices = eastGeometry.indices;
                var p0IndexMap = eastGeometryIndexMap;
                var p1Attributes = westGeometry.attributes;
                var p1Indices = westGeometry.indices;
                var p1IndexMap = westGeometryIndexMap;

                var intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(p0, p1, xzPlane, p2Scratch);
                if (when.defined(intersection)) {
                    // move point on the xz-plane slightly away from the plane
                    var offset = Cartesian2.Cartesian3.multiplyByScalar(Cartesian2.Cartesian3.UNIT_Y, 5.0 * _Math.CesiumMath.EPSILON9, offsetScratch);
                    if (p0.y < 0.0) {
                        Cartesian2.Cartesian3.negate(offset, offset);

                        p0Attributes = westGeometry.attributes;
                        p0Indices = westGeometry.indices;
                        p0IndexMap = westGeometryIndexMap;
                        p1Attributes = eastGeometry.attributes;
                        p1Indices = eastGeometry.indices;
                        p1IndexMap = eastGeometryIndexMap;
                    }

                    var offsetPoint = Cartesian2.Cartesian3.add(intersection, offset, offsetPointScratch);

                    insertIndex = insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, i, p0);
                    computeLineAttributes(i0, i1, p0, positions, insertIndex, p0Attributes, applyOffset);

                    insertIndex = insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, -1, offsetPoint);
                    computeLineAttributes(i0, i1, offsetPoint, positions, insertIndex, p0Attributes, applyOffset);

                    Cartesian2.Cartesian3.negate(offset, offset);
                    Cartesian2.Cartesian3.add(intersection, offset, offsetPoint);
                    insertIndex = insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, -1, offsetPoint);
                    computeLineAttributes(i0, i1, offsetPoint, positions, insertIndex, p1Attributes, applyOffset);

                    insertIndex = insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, i + 1, p1);
                    computeLineAttributes(i0, i1, p1, positions, insertIndex, p1Attributes, applyOffset);
                } else {
                    var currentAttributes;
                    var currentIndices;
                    var currentIndexMap;

                    if (p0.y < 0.0) {
                        currentAttributes = westGeometry.attributes;
                        currentIndices = westGeometry.indices;
                        currentIndexMap = westGeometryIndexMap;
                    } else {
                        currentAttributes = eastGeometry.attributes;
                        currentIndices = eastGeometry.indices;
                        currentIndexMap = eastGeometryIndexMap;
                    }

                    insertIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i, p0);
                    computeLineAttributes(i0, i1, p0, positions, insertIndex, currentAttributes, applyOffset);

                    insertIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1);
                    computeLineAttributes(i0, i1, p1, positions, insertIndex, currentAttributes, applyOffset);
                }
            }

            updateInstanceAfterSplit(instance, westGeometry, eastGeometry);
        }

        var cartesian2Scratch0 = new Cartesian2.Cartesian2();
        var cartesian2Scratch1 = new Cartesian2.Cartesian2();

        var cartesian3Scratch0 = new Cartesian2.Cartesian3();
        var cartesian3Scratch2 = new Cartesian2.Cartesian3();
        var cartesian3Scratch3 = new Cartesian2.Cartesian3();
        var cartesian3Scratch4 = new Cartesian2.Cartesian3();
        var cartesian3Scratch5 = new Cartesian2.Cartesian3();
        var cartesian3Scratch6 = new Cartesian2.Cartesian3();

        var cartesian4Scratch0 = new Transforms.Cartesian4();

        function updateAdjacencyAfterSplit(geometry) {
            var attributes = geometry.attributes;
            var positions = attributes.position.values;
            var prevPositions = attributes.prevPosition.values;
            var nextPositions = attributes.nextPosition.values;

            var length = positions.length;
            for (var j = 0; j < length; j += 3) {
                var position = Cartesian2.Cartesian3.unpack(positions, j, cartesian3Scratch0);
                if (position.x > 0.0) {
                    continue;
                }

                var prevPosition = Cartesian2.Cartesian3.unpack(prevPositions, j, cartesian3Scratch2);
                if ((position.y < 0.0 && prevPosition.y > 0.0) || (position.y > 0.0 && prevPosition.y < 0.0)) {
                    if (j - 3 > 0) {
                        prevPositions[j] = positions[j - 3];
                        prevPositions[j + 1] = positions[j - 2];
                        prevPositions[j + 2] = positions[j - 1];
                    } else {
                        Cartesian2.Cartesian3.pack(position, prevPositions, j);
                    }
                }

                var nextPosition = Cartesian2.Cartesian3.unpack(nextPositions, j, cartesian3Scratch3);
                if ((position.y < 0.0 && nextPosition.y > 0.0) || (position.y > 0.0 && nextPosition.y < 0.0)) {
                    if (j + 3 < length) {
                        nextPositions[j] = positions[j + 3];
                        nextPositions[j + 1] = positions[j + 4];
                        nextPositions[j + 2] = positions[j + 5];
                    } else {
                        Cartesian2.Cartesian3.pack(position, nextPositions, j);
                    }
                }
            }
        }

        var offsetScalar = 5.0 * _Math.CesiumMath.EPSILON9;
        var coplanarOffset = _Math.CesiumMath.EPSILON6;

        function splitLongitudePolyline(instance) {
            var geometry = instance.geometry;
            var attributes = geometry.attributes;
            var positions = attributes.position.values;
            var prevPositions = attributes.prevPosition.values;
            var nextPositions = attributes.nextPosition.values;
            var expandAndWidths = attributes.expandAndWidth.values;

            var texCoords = (when.defined(attributes.st)) ? attributes.st.values : undefined;
            var colors = (when.defined(attributes.color)) ? attributes.color.values : undefined;

            var eastGeometry = copyGeometryForSplit(geometry);
            var westGeometry = copyGeometryForSplit(geometry);

            var i;
            var j;
            var index;

            var intersectionFound = false;

            var length = positions.length / 3;
            for (i = 0; i < length; i += 4) {
                var i0 = i;
                var i2 = i + 2;

                var p0 = Cartesian2.Cartesian3.fromArray(positions, i0 * 3, cartesian3Scratch0);
                var p2 = Cartesian2.Cartesian3.fromArray(positions, i2 * 3, cartesian3Scratch2);

                // Offset points that are close to the 180 longitude and change the previous/next point
                // to be the same offset point so it can be projected to 2D. There is special handling in the
                // shader for when position == prevPosition || position == nextPosition.
                if (Math.abs(p0.y) < coplanarOffset) {
                    p0.y = coplanarOffset * (p2.y < 0.0 ? -1.0 : 1.0);
                    positions[i * 3 + 1] = p0.y;
                    positions[(i + 1) * 3 + 1] = p0.y;

                    for (j = i0 * 3; j < i0 * 3 + 4 * 3; j += 3) {
                        prevPositions[j] = positions[i * 3];
                        prevPositions[j + 1] = positions[i * 3 + 1];
                        prevPositions[j + 2] = positions[i * 3 + 2];
                    }
                }

                // Do the same but for when the line crosses 180 longitude in the opposite direction.
                if (Math.abs(p2.y) < coplanarOffset) {
                    p2.y = coplanarOffset * (p0.y < 0.0 ? -1.0 : 1.0);
                    positions[(i + 2) * 3 + 1] = p2.y;
                    positions[(i + 3) * 3 + 1] = p2.y;

                    for (j = i0 * 3; j < i0 * 3 + 4 * 3; j += 3) {
                        nextPositions[j] = positions[(i + 2) * 3];
                        nextPositions[j + 1] = positions[(i + 2) * 3 + 1];
                        nextPositions[j + 2] = positions[(i + 2) * 3 + 2];
                    }
                }

                var p0Attributes = eastGeometry.attributes;
                var p0Indices = eastGeometry.indices;
                var p2Attributes = westGeometry.attributes;
                var p2Indices = westGeometry.indices;

                var intersection = IntersectionTests.IntersectionTests.lineSegmentPlane(p0, p2, xzPlane, cartesian3Scratch4);
                if (when.defined(intersection)) {
                    intersectionFound = true;

                    // move point on the xz-plane slightly away from the plane
                    var offset = Cartesian2.Cartesian3.multiplyByScalar(Cartesian2.Cartesian3.UNIT_Y, offsetScalar, cartesian3Scratch5);
                    if (p0.y < 0.0) {
                        Cartesian2.Cartesian3.negate(offset, offset);
                        p0Attributes = westGeometry.attributes;
                        p0Indices = westGeometry.indices;
                        p2Attributes = eastGeometry.attributes;
                        p2Indices = eastGeometry.indices;
                    }

                    var offsetPoint = Cartesian2.Cartesian3.add(intersection, offset, cartesian3Scratch6);
                    p0Attributes.position.values.push(p0.x, p0.y, p0.z, p0.x, p0.y, p0.z);
                    p0Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p0Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);

                    p0Attributes.prevPosition.values.push(prevPositions[i0 * 3], prevPositions[i0 * 3 + 1], prevPositions[i0 * 3 + 2]);
                    p0Attributes.prevPosition.values.push(prevPositions[i0 * 3 + 3], prevPositions[i0 * 3 + 4], prevPositions[i0 * 3 + 5]);
                    p0Attributes.prevPosition.values.push(p0.x, p0.y, p0.z, p0.x, p0.y, p0.z);

                    p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p0Attributes.nextPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);

                    Cartesian2.Cartesian3.negate(offset, offset);
                    Cartesian2.Cartesian3.add(intersection, offset, offsetPoint);
                    p2Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p2Attributes.position.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p2Attributes.position.values.push(p2.x, p2.y, p2.z, p2.x, p2.y, p2.z);

                    p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);
                    p2Attributes.prevPosition.values.push(offsetPoint.x, offsetPoint.y, offsetPoint.z);

                    p2Attributes.nextPosition.values.push(p2.x, p2.y, p2.z, p2.x, p2.y, p2.z);
                    p2Attributes.nextPosition.values.push(nextPositions[i2 * 3], nextPositions[i2 * 3 + 1], nextPositions[i2 * 3 + 2]);
                    p2Attributes.nextPosition.values.push(nextPositions[i2 * 3 + 3], nextPositions[i2 * 3 + 4], nextPositions[i2 * 3 + 5]);

                    var ew0 = Cartesian2.Cartesian2.fromArray(expandAndWidths, i0 * 2, cartesian2Scratch0);
                    var width = Math.abs(ew0.y);

                    p0Attributes.expandAndWidth.values.push(-1,  width, 1,  width);
                    p0Attributes.expandAndWidth.values.push(-1, -width, 1, -width);
                    p2Attributes.expandAndWidth.values.push(-1,  width, 1,  width);
                    p2Attributes.expandAndWidth.values.push(-1, -width, 1, -width);

                    var t = Cartesian2.Cartesian3.magnitudeSquared(Cartesian2.Cartesian3.subtract(intersection, p0, cartesian3Scratch3));
                    t /= Cartesian2.Cartesian3.magnitudeSquared(Cartesian2.Cartesian3.subtract(p2, p0, cartesian3Scratch3));

                    if (when.defined(colors)) {
                        var c0 = Transforms.Cartesian4.fromArray(colors, i0 * 4, cartesian4Scratch0);
                        var c2 = Transforms.Cartesian4.fromArray(colors, i2 * 4, cartesian4Scratch0);

                        var r = _Math.CesiumMath.lerp(c0.x, c2.x, t);
                        var g = _Math.CesiumMath.lerp(c0.y, c2.y, t);
                        var b = _Math.CesiumMath.lerp(c0.z, c2.z, t);
                        var a = _Math.CesiumMath.lerp(c0.w, c2.w, t);

                        for (j = i0 * 4; j < i0 * 4 + 2 * 4; ++j) {
                            p0Attributes.color.values.push(colors[j]);
                        }
                        p0Attributes.color.values.push(r, g, b, a);
                        p0Attributes.color.values.push(r, g, b, a);
                        p2Attributes.color.values.push(r, g, b, a);
                        p2Attributes.color.values.push(r, g, b, a);
                        for (j = i2 * 4; j < i2 * 4 + 2 * 4; ++j) {
                            p2Attributes.color.values.push(colors[j]);
                        }
                    }

                    if (when.defined(texCoords)) {
                        var s0 = Cartesian2.Cartesian2.fromArray(texCoords, i0 * 2, cartesian2Scratch0);
                        var s3 = Cartesian2.Cartesian2.fromArray(texCoords, (i + 3) * 2, cartesian2Scratch1);

                        var sx = _Math.CesiumMath.lerp(s0.x, s3.x, t);

                        for (j = i0 * 2; j < i0 * 2 + 2 * 2; ++j) {
                            p0Attributes.st.values.push(texCoords[j]);
                        }
                        p0Attributes.st.values.push(sx, s0.y);
                        p0Attributes.st.values.push(sx, s3.y);
                        p2Attributes.st.values.push(sx, s0.y);
                        p2Attributes.st.values.push(sx, s3.y);
                        for (j = i2 * 2; j < i2 * 2 + 2 * 2; ++j) {
                            p2Attributes.st.values.push(texCoords[j]);
                        }
                    }

                    index = p0Attributes.position.values.length / 3 - 4;
                    p0Indices.push(index, index + 2, index + 1);
                    p0Indices.push(index + 1, index + 2, index + 3);

                    index = p2Attributes.position.values.length / 3 - 4;
                    p2Indices.push(index, index + 2, index + 1);
                    p2Indices.push(index + 1, index + 2, index + 3);
                } else {
                    var currentAttributes;
                    var currentIndices;

                    if (p0.y < 0.0) {
                        currentAttributes = westGeometry.attributes;
                        currentIndices = westGeometry.indices;
                    } else {
                        currentAttributes = eastGeometry.attributes;
                        currentIndices = eastGeometry.indices;
                    }

                    currentAttributes.position.values.push(p0.x, p0.y, p0.z);
                    currentAttributes.position.values.push(p0.x, p0.y, p0.z);
                    currentAttributes.position.values.push(p2.x, p2.y, p2.z);
                    currentAttributes.position.values.push(p2.x, p2.y, p2.z);

                    for (j = i * 3; j < i * 3 + 4 * 3; ++j) {
                        currentAttributes.prevPosition.values.push(prevPositions[j]);
                        currentAttributes.nextPosition.values.push(nextPositions[j]);
                    }

                    for (j = i * 2; j < i * 2 + 4 * 2; ++j) {
                        currentAttributes.expandAndWidth.values.push(expandAndWidths[j]);
                        if (when.defined(texCoords)) {
                            currentAttributes.st.values.push(texCoords[j]);
                        }
                    }

                    if (when.defined(colors)) {
                        for (j = i * 4; j < i * 4 + 4 * 4; ++j) {
                            currentAttributes.color.values.push(colors[j]);
                        }
                    }

                    index = currentAttributes.position.values.length / 3 - 4;
                    currentIndices.push(index, index + 2, index + 1);
                    currentIndices.push(index + 1, index + 2, index + 3);
                }
            }

            if (intersectionFound) {
                updateAdjacencyAfterSplit(westGeometry);
                updateAdjacencyAfterSplit(eastGeometry);
            }

            updateInstanceAfterSplit(instance, westGeometry, eastGeometry);
        }

        /**
         * Splits the instances's geometry, by introducing new vertices and indices,that
         * intersect the International Date Line and Prime Meridian so that no primitives cross longitude
         * -180/180 degrees.  This is not required for 3D drawing, but is required for
         * correcting drawing in 2D and Columbus view.
         *
         * @private
         *
         * @param {GeometryInstance} instance The instance to modify.
         * @returns {GeometryInstance} The modified <code>instance</code> argument, with it's geometry split at the International Date Line.
         *
         * @example
         * instance = Cesium.GeometryPipeline.splitLongitude(instance);
         */
        GeometryPipeline.splitLongitude = function(instance) {
            //>>includeStart('debug', pragmas.debug);
            if (!when.defined(instance)) {
                throw new Check.DeveloperError('instance is required.');
            }
            //>>includeEnd('debug');

            var geometry = instance.geometry;
            var boundingSphere = geometry.boundingSphere;
            if (when.defined(boundingSphere)) {
                var minX = boundingSphere.center.x - boundingSphere.radius;
                if (minX > 0 || Transforms.BoundingSphere.intersectPlane(boundingSphere, Plane.Plane.ORIGIN_ZX_PLANE) !== Transforms.Intersect.INTERSECTING) {
                    return instance;
                }
            }

            if (geometry.geometryType !== GeometryAttribute.GeometryType.NONE) {
                switch (geometry.geometryType) {
                case GeometryAttribute.GeometryType.POLYLINES:
                    splitLongitudePolyline(instance);
                    break;
                case GeometryAttribute.GeometryType.TRIANGLES:
                    splitLongitudeTriangles(instance);
                    break;
                case GeometryAttribute.GeometryType.LINES:
                    splitLongitudeLines(instance);
                    break;
                }
            } else {
                indexPrimitive(geometry);
                if (geometry.primitiveType === GeometryAttribute.PrimitiveType.TRIANGLES) {
                    splitLongitudeTriangles(instance);
                } else if (geometry.primitiveType === GeometryAttribute.PrimitiveType.LINES) {
                    splitLongitudeLines(instance);
                }
            }

            return instance;
        };

    exports.GeometryPipeline = GeometryPipeline;

});
